我正在尝试使用 Spring Data Mongo 的聚合 API 进行简单的投影。
我想做的管道步骤是:
{
$project : {
"account._id" : 1,
"account.position" : 1
}
}
这是我尝试过的(以及大量其他调整,因为似乎没有任何效果):
ProjectionOperation project1 = Aggregation.project("account._id", "account.position");
但是,尽管文档是这样说的:https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation.projection
由该投影渲染的实际文档最终看起来像:
{
$project : {
_id : "$account._id",
position : "$account.position"
}
}
其工作原理与我想要使用的投影完全不同。
有谁知道如何从 Spring Data Mongo Aggregation API 中获得我想要的投影,或者这是我需要报告的错误?
2019 年 8 月 29 日更新 - 添加更多数据来构建上下文:
涉及两个集合:“组”和“账户” 一个组看起来像这样:
{
_id : ObjectId("..."),
name: ...,
ownerId: ObjectId("..."),
other stuff...
}
帐户看起来像这样:
{
_id : ObjectId("..."),
position : "ABC",
memberships : [{
groupId: ObjectId("..."),
otherstuff: ...,
}],
other stuff...
}
我的整个聚合看起来像这样,并在 mongodb shell 中按需要工作:(尝试获取特定类型的所有帐户 ID 的列表,这些帐户 ID 是特定用户拥有的任何组的成员)
groups.aggregate(
{
$match : {
ownerId : ObjectId("XYZ"),
}
},
{
$lookup: {
from: "accounts",
localField: "_id",
foreignField: "memberships.groupId",
as: "account"
}
},
{
$project: {
"account._id" : 1,
"account.position" : 1
}
},
{
$unwind: "$account"
},
{
$match: {
"account.position" : "ZZZ"
}
},
{
$project: {
_id : 0,
accountId : "$account._id"
}
})
Java 版本的聚合:
MatchOperation match1 = Aggregation.match(
where("ownerId").is(accountId));
LookupOperation lookupOperation = LookupOperation.newLookup()
.from("accounts")
.localField("_id")
.foreignField("memberships.groupId")
.as("account");
// This doesn't work correctly on nested fields:
ProjectionOperation project1 = Aggregation.project(
"studentAccount._id",
"studentAccount.position");
Aggregation aggregation = Aggregation.newAggregation(
match1,
lookupOperation,
project1,
unwind("account"),
match(where("account.position").is("ZZZ")),
project().and("account._id").as("accountId"));
如果你希望你的聚合工作看起来像 mongoshell 你可以尝试这样
Aggregation aggregation = Aggregation.newAggregation(
match1,
lookupOperation,
// This's your project operation
new AggregationOperation() {
@Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
Document project = new Document("$project",
new Document(
"_id", "$account._id"
).append("position", "$account.position")
);
return aggregationOperationContext.getMappedObject(project);
}
},
unwind("account"),
match(where("account.position").is("ZZZ")),
project().and("account._id").as("accountId")
);
您可以在此处以更通用的方式查看我的答案
Spring Aggregation API 的解决方案是:
Aggregation.project(Fields.from(
Fields.field("account._id", "account._id"),
Fields.field("account.position", "account.position"),
));
这不是很直观,但是 spring 中的字段有一个名称和一个目标。如果路径不包含点,则它们相等,否则名称默认为最后一个路径条目(在最后一个点之后)。
您还可以利用这一点来重命名字段:
Aggregation.project(Fields.from(Fields.field("a", "b")));
将会导致
{
"$project": { "a": "$b" }
}