【问题标题】:MongoDB with Java async driver: $lookup always empty带有 Java 异步驱动程序的 MongoDB:$lookup 始终为空
【发布时间】:2017-11-28 13:26:39
【问题描述】:

我有这两个“链接”的 POJO 实体:

public class User {
    public ObjectId id; // this is mapped to "_id" in the MongoDB
    public String userName;
    ...
}

public class Purchase {
    public ObjectId id; // this is mapped to "_id" in the MongoDB
    public ObjectId userId;
    public transient User user;

    public String productTitle;
    ...
}

我们的想法是只保留用于购买的用户 ID,并使用 $lookup 聚合函数按需加载(或:加入)适当的用户文档。因为 Purchase.user 属性不应该保存在 MongoDB 中,所以它被声明为瞬态。这能行吗?

现在,在我的 PurchaseRepository 中,我正在尝试像这样实现它:

public void getSinglePurchaseWithUser(Bson filter, SingleResultCallback<Purchase> callback) {
   Document match = new Document("$match", filter);

   Document lookupFields = new Document("from", "Users");
   lookupFields.put("localField", "userId");
   lookupFields.put("foreignField", "_id");
   lookupFields.put("as", "user");
   Document lookup = new Document("$lookup", lookupFields);

   List<Document> pipeline = Arrays.asList(match, lookup);

   AggregateIterable<Purchase> output = this.collection.aggregate(pipeline);
   output.first(callback);
}

很遗憾,purchase.user 在结果中始终为空。我还尝试手动投影来明确地读取用户:

Document projectFields = new Document("_id", 1);
projectFields.put("userId", 1);
projectFields.put("user", "$user");
...
Document project = new Document("$project", projectFields);

List<Document> pipeline = Arrays.asList(match, lookup, project);

但这会引发错误读取:

org.bson.codecs.configuration.CodecConfigurationException:一个 使用 AutomaticPojoCodec 解码时发生异常。 解码为“购买”失败,但出现以下异常:

解码“用户”失败。 readStartDocument 只能在以下情况下调用 CurrentBSONType 是 DOCUMENT,而不是当 CurrentBSONType 是 ARRAY 时。

自定义编解码器或 PojoCodec 可能需要显式配置和 注册来处理这种类型。

我错过了什么?

【问题讨论】:

    标签: java mongodb mongodb-query


    【解决方案1】:

    知道了。

    第一个错误是“as”字段必须是一个数组:

    指定要添加到输入的新数组字段的名称 文件。新的数组字段包含来自的匹配文档 从集合。如果输入中已经存在指定的名称 文档,现有字段将被覆盖。

    另一个问题是transient 属性既没有序列化也没有反序列化。解决此问题的方法是将 getter 标记为 @BsonIgnore 而不是 setter。这样,在序列化时会跳过该属性,但在反序列化时不会跳过该属性。

    public class Purchase {
        private User user;
        public ObjectId userId;
    
        @BsonIgnore
        public OrgenicUser getUser() {
            return this.user;
        }
    
        public void setUser(OrgenicUser value) {
            this.user = value;
        }
    }
    

    如果您想将数组限制为像我这样的单个对象,您可以在管道中使用$getElemAt 来拉取第一个条目,如下所示:

    Document lookupFields = new Document("from", from);
    lookupFields.put("localField", localField);
    lookupFields.put("foreignField", foreignField);
    lookupFields.put("as", as);
    Document lookup = new Document("$lookup", lookupFields);
    
    // pulls the first match from the localField array
    Document firstMatch = new Document(as, new Document("$arrayElemAt", Arrays.asList("$" + as, 0)));
    // keep all fields and add/update the specified field
    Document project = new Document("$addFields", firstMatch);
    

    【讨论】:

      猜你喜欢
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多