我有这两个"链接" 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;
...
}
我们的想法是只保留UserId以进行购买,并使用$ lookup聚合函数按需加载(或:JOIN)相应的用户文档。因为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);
}
不幸的是,buy.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:An 使用AutomaticPojoCodec解码时发生异常。 解码为&#39;购买&#39;失败,出现以下异常:
无法解码用户&#39;。 readStartDocument只能在调用时调用 CurrentBSONType是DOCUMENT,而不是当CurrentBSONType是ARRAY时。
可能需要显式配置自定义编解码器或PojoCodec 注册处理此类型。
我错过了什么?
答案 0 :(得分:0)
知道了。
第一个错误是&#34; as&#34; fields必须是一个数组:
指定要添加到输入的新数组字段的名称 文档。新数组字段包含来自的匹配文档 来自收藏。如果输入中已存在指定的名称 文档,现有字段被覆盖。
另一个问题是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;
}
}
如果你想将数组限制为一个单独的对象,就像在我的情况下一样,你可以在你的pipepline中使用$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);