首页 > 解决方案 > 如何让 Spring Data Mongo 聚合像使用 mongo 一样工作

问题描述

我正在尝试使用 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"));

标签: mongodbaggregation-frameworkspring-data-mongodb

解决方案


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

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


推荐阅读