如何让 Spring Data Mongo 聚合像使用 mongo 一样工作

问题描述 投票:0回答:2

我正在尝试使用 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"));
mongodb aggregation-framework spring-data-mongodb
2个回答
1
投票

如果你希望你的聚合工作看起来像 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")
        );

您可以在此处以更通用的方式查看我的答案


0
投票

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" }
}
© www.soinside.com 2019 - 2024. All rights reserved.