在Mongo的分组聚合后,是否可以重命名_id字段?

65

我有一个类似这样的查询(简化版):

db.collection.aggregate([
  { $match: { main_id: ObjectId("58f0f67f50c6af16709fd2c7") } }, 
  {
    $group: {
      _id: "$name",
      count: { $sum: 1 },
      sum: { $sum: { $add: ["$P31", "$P32"] } }
    }
  }
])

我在Java中执行这个查询,并且想要将其映射到我的类上,但是我不想让_id映射到name字段。因为如果我这样做:

@JsonProperty("_id")
private String name;

接着,当我对这些数据进行了一些修改后,将其保存回Mongo时,数据的名称被保存为_id,而我希望生成一个真正的Id。

那么,在$group操作之后,我如何重命名_id呢?


2
$project 在管道的下一阶段中使用。 - Alex Blex
{ $addFields: { newName: "$_id" } }, { $project: { _id: 1 } } - artemitSoft
7个回答

109

37
从mongo v3.4开始,您可以在使用$project时与$addFields配合使用,以避免编写所有可能非常繁琐的字段。$project中出现这种情况是因为如果您明确包含一个字段,则其他字段将自动被排除。
示例:
{ 
  $addFields: { my_new_id_name: "$_id" }
},
{
  $project: { _id: 0 }
}

对我来说没有用,我得到了“$projection至少需要一个输出字段。”(在聚合中使用它) - Gonzalo.-
澄清:我正在使用 AWS 中的 DocumentDB。 - Gonzalo.-
它的功能良好,但我需要将_id转换为字符串,因此应该使用{ $addFields: { id: { $toString: '$_id' } } },并且应该尊重管道的顺序,先使用$addFields$project - drocha87

5
 db.report.aggregate(   
{     
$group: {_id: '$name'} 
},
{
$project:{
  name:"$_id",
 _id:false} }
 )

4

Mongo 4.2 开始,您可以使用组合的 $set / $unset 阶段:

// { x: 1, z: "a" }
// { x: 2, z: "b" }
db.collection.aggregate([
  { $set: { y: "$x" } },
  { $unset: "x" }
])
// { y: 1, z: "a" }
// { y: 2, z: "b" }

$set 阶段将新字段添加到文档中,而 $unset 阶段则从文档中移除/排除要重命名的字段。


1
如果您正在使用find方法,则无法做到这一点,但如果您使用aggregation,则像这样非常容易:
db.collectionName.aggregate([
    {
        $project: {
            newName: "$existingKeyName"
        }
    }
]);

1

以下是使用MongoTemplate的Java工作示例:

GroupOperation groupByMovie = Aggregation.group("movieId")
                .avg("rating").as("averageRating");

        SortOperation sortByAvgRatingDesc = Aggregation.sort(Sort.Direction.DESC, "averageRating");
        LimitOperation limitRows = Aggregation.limit(limit);
        ProjectionOperation projectionOperation = Aggregation.project()
                .and("_id").as("movieId")
                .and("averageRating").as("averageRating")
                .andExclude("_id");

        Aggregation aggregation = Aggregation.newAggregation(
                groupByMovie,
                projectionOperation,
                sortByAvgRatingDesc,
                limitRows
        );

        AggregationResults<TopRatedMovie> results = mongoTemplate.aggregate(aggregation, MovieRating.class, TopRatedMovie.class);

        return results.getMappedResults();

0

尽管问题要求使用Java解决方案,但所有答案都是用MongoDB查询编写的解决方案。为了记录,我将使用Java来介绍我的方法。

在分组之后,我们可以使用Projections.computed("<期望的字段名>", "$_id"))来重命名_id字段名。

将问题中提到的查询的核心部分转换为Java:

        Bson mainIdMatch = match(eq("main_id", new ObjectId("58f0f67f50c6af16709fd2c7")));
        Bson group = Aggregates.group("$name", Accumulators.sum("count", 1L));
        Bson project = Aggregates.project(Projections.fields(Projections.excludeId(),
                Projections.computed("name", "$_id")));
        reportMongoCollection.aggregate(Arrays.asList(mainIdMatch, group, project))
                .into(new ArrayList<>());

具体回答,我已从上面的代码片段中添加了一段摘录,其中我将_id字段值重命名为名称,使用Projections.computed("name", "$_id")将我们作为分组结果获得的_id的值映射到名为name的字段。此外,我们应该使用Projections.excludeId()来排除id。
Aggregates.project(Projections.fields(Projections.excludeId(),
                Projections.computed("name", "$_id")))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接