当前位置: 首页 > 知识库问答 >
问题:

如何在Spring Data MongoDB中实现聚合查询?

魏威
2023-03-14

我是Spring Data MongoDB的新手,我正在尝试用Spring Data MongoDBJava实现聚合查询。我尝试过从这个问题中搜索,并使用MongoTemboard进行搜索,但仍然没有结果。

我的数据格式:

[{ 
    "_id" : ObjectId("5e1aea6c275360baf96bac29"), 
    "title" : "postim", 
    "upvotesBy" : [
        "5e18b4c12753608718dfa007", 
        "5e19ac0f5161a4994ded1f35"
    ], 
    "file" : "test", 
    "description" : "description", 
    "postedBy" : "5e18b4c12753608718dfa007", 
    "createdAt" : ISODate("2020-01-12T09:44:12.119+0000"), 
    "_class" : "com.socialnetwork.post.Post"
},
{ 
    "_id" : ObjectId("5e1aeaf8275360bb4bb47325"), 
    "title" : "postim2", 
    "upvotesBy" : [
        "5e18b4c12753608718dfa007", 
        "5e19ac0f5161a4994ded1f35"
    ], 
    "file" : "test2", 
    "description" : "description2", 
    "postedBy" : "5e18b4c12753608718dfa007", 
    "createdAt" : ISODate("2020-01-12T09:46:32.909+0000"), 
    "_class" : "com.socialnetwork.post.Post"
}]

我的查询:

db.post.aggregate([
    {
      $match: {}
    },
    {
      $lookup: {
        from: "users",
        localField: "postedBy",
        foreignField: "_id",
        as: "user"
      }
    },
    {
      $group: {
        _id: {
          username: "$user.name",
          title: "$title",
          description: "$description",
          upvotes: { $size: "$upvotesBy" },
          upvotesBy: "$upvotesBy",
          isUpvoted: { $in: [req.query.userId, "$upvotesBy"] },
          isPinned: {
            $cond: {
              if: { $gte: [{ $size: "$upvotesBy" }, 3] },
              then: true,
              else: false
            }
          },
          file: "$file",
          createdAt: {
            $dateToString: {
              format: "%H:%M %d-%m-%Y",
              timezone: "+01",
              date: "$createdAt"
            }
          },
          id: "$_id"
        }
      }
    },
    { $sort: { "_id.isPinned": -1, "_id.createdAt": -1 } }
])

这是我在Javascript后端使用的查询,我可以用Mongoose很容易地做到这一点。然而,我对它的Java实现有一些困难。

private LookupOperation getLookupOperation() {
        return LookupOperation.newLookup().from("user")
                .localField("postedBy")
                .foreignField("_id")
                .as("user");
    }

    @Override
    public List<PostSummary> aggregate() {
        LookupOperation lookupOperation = getLookupOperation();
        return mongoTemplate.aggregate(Aggregation.newAggregation(lookupOperation, Aggregation.group("id")
                .addToSet("user.name").as("username")
                .addToSet("title").as("title")
                .addToSet("description").as("description")
                .addToSet("id").as("id")
                .push("upvotesBy").as("upvotesBy")
                .addToSet("file").as("file")
                .addToSet("createdAt").as("createdAt")
        ), Post.class, PostSummary.class).getMappedResults();
}

当我尝试运行此程序时,会出现以下错误:

"Cannot convert [] of type class java.util.ArrayList into an instance of class java.lang.Object! Implement a custom Converter<class java.util.ArrayList, class java.lang.Object> and register it with the CustomConversions. Parent object was: com.socialnetwork.post.PostSummary@7159d908"

当我删除时。addToSet(“user.name”)。作为(“用户名”)从组聚合中,我还从中得到一个错误。推(“上浮”)。as(“upvotesBy”),因为它不能转换java类的[]类型。util。将ArrayList放入java类的实例中。lang.String

Post类和PostSummary类的实现也很简单:

Post。java

@Document
public class Post {
    @Id
    private String id;
    private String title;
    private List<String> upvotesBy;
    private String file;
    private String description;
    private String postedBy;
    private Date createdAt = new Date();

//  ... Getters and Setters for each field
}

PostSummary。java

public class PostSummary {
    private String username;
    private String title;
    private String description;
    private List<String> upvotesBy;
    private String file;
    private String createdAt;
    private String id;

//... Getters and Setters for the class
}

我还需要实现查询的isUpvotedisPinned部分,但是了解如何处理第一个问题将是一个很好的开始。

编辑:我想要的输出:

[
{
   "username" : "user1", 
   "title" : "postim2", 
   "upvotesBy" : [
      "5e18b4c12753608718dfa007", 
      "5e19ac0f5161a4994ded1f35"
   ],
   "file": "file1",
   id: "5e18b4c12753608718dber01"
   ... Other fields of the original post
},
{
   "username" : "user2", 
   "title" : "postim2", 
   "upvotesBy" : [
      "5e18b4c12753608718dfa007", 
      "5e19ac0f5161a4994ded1f35"
   ],
   id: "5e18b4c12753608718dber02",
   "file": "file2",
   ... Other fields of the original post
}
]

因此,从查找操作中,我只需要获得用户的名称。

共有1个答案

桂坚
2023-03-14

我们需要更新您的聚合以使其正常工作。

错误:

  1. users\u idObjectId类型,但在您的帖子中,您已存储为字符串,因此$lookup应更改为不相关的子查询
db.post.aggregate([
  {
    $match: {}
  },
  {
    $lookup: {
      from: "users",
      let: {
        postedBy: "$postedBy"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                {
                  "$toString": "$_id"
                },
                "$$postedBy"
              ]
            }
          }
        }
      ],
      as: "user"
    }
  },
  {
    $unwind: "$user"
  },
  {
    $addFields: {
      id: {
        $toString: "$_id"
      },
      username: "$user.name",
      upvotes: {
        $size: "$upvotesBy"
      },
      isUpvoted: {
        $in: [
          "5e18b4c12753608718dfa007",
          "$upvotesBy"
        ]
      },
      isPinned: {
        $cond: [
          {
            $gte: [
              {
                $size: "$upvotesBy"
              },
              3
            ]
          },
          true,
          false
        ]
      },
      createdAt: {
        $dateToString: {
          format: "%H:%M %d-%m-%Y",
          timezone: "+01",
          date: "$createdAt"
        }
      }
    }
  },
  {
    $sort: {
      "isPinned": -1,
      "createdAt": -1
    }
  },
  {
    $project: {
      _id: 0,
      user: 0,
      upvotesBy: 0,
      _class: 0
    }
  }
])

现在,我们将此查询转换为Spring数据语法。

package postman;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.sort;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.unwind;

import java.util.Arrays;
import java.util.List;

import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

@Service
public class PostmanService {

    @Autowired
    private MongoTemplate mongoTemplate;

    public List<PostSummary> find(String userId){

        Aggregation aggregation = Aggregation.newAggregation(
            match(new Criteria()),
            //lookup("users", "postedBy", "_id", "user")
            new AggregationOperation() {
                @Override
                public Document toDocument(AggregationOperationContext context) {
                    return new Document("$lookup",
                        new Document("from", "users")
                            .append("let", new Document("postedBy", "$postedBy"))
                            .append("pipeline", Arrays.asList(
                                new Document("$match", 
                                    new Document("$expr", 
                                        new Document("$eq", Arrays.asList(
                                            new Document("$toString", "$_id"),
                                            "$$postedBy"
                                        ))))))
                            .append("as", "user"));
                }
            },
            unwind("$user"),
            new AggregationOperation() {

                @Override
                public Document toDocument(AggregationOperationContext context) {
                    return new Document("$addFields",
                        new Document("id", new Document("$toString", "$_id"))
                        .append("username", "$user.name")
                        .append("upvotes", new Document("$size", "$upvotesBy"))
                        .append("isUpvoted", new Document("$in", Arrays.asList(userId, "$upvotesBy")))
                        .append("isPinned", new Document("$cond", 
                            Arrays.asList(new Document("$gte", 
                                    Arrays.asList(new Document("$size", "$upvotesBy"), 3)), Boolean.TRUE, Boolean.FALSE)))
                        .append("createdAt", new Document("$dateToString", 
                            new Document("format", "%H:%M %d-%m-%Y")
                                .append("timezone", "+01")
                                .append("date", "$createdAt")
                            )));
                }
            },
            sort(Direction.DESC, "isPinned", "createdAt"),
            project().andExclude("user", "_class")
        );

        System.out.println("Aggregation: " + aggregation.toString());

        return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Post.class), PostSummary.class).getMappedResults();
    }
}

现在,我们称之为聚合管道:

List<PostSummary> l = postmanService.find("5e18b4c12753608718dfa007");
for(PostSummary post: l) {
    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
    System.out.println(ow.writeValueAsString(post));
}
2020-01-12 16:15:22.043  INFO 11148 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-01-12 16:15:22.047  INFO 11148 --- [           main] Postman.PostmanApplication               : Started PostmanApplication in 4.602 seconds (JVM running for 5.301)
Aggregation: { "aggregate" : "__collection__", "pipeline" : [{ "$match" : {}}, { "$lookup" : { "from" : "users", "let" : { "postedBy" : "$postedBy"}, "pipeline" : [{ "$match" : { "$expr" : { "$eq" : [{ "$toString" : "$_id"}, "$$postedBy"]}}}], "as" : "user"}}, { "$unwind" : "$user"}, { "$addFields" : { "id" : { "$toString" : "$_id"}, "username" : "$user.name", "upvotes" : { "$size" : "$upvotesBy"}, "isUpvoted" : { "$in" : ["5e18b4c12753608718dfa007", "$upvotesBy"]}, "isPinned" : { "$cond" : [{ "$gte" : [{ "$size" : "$upvotesBy"}, 3]}, true, false]}, "createdAt" : { "$dateToString" : { "format" : "%H:%M %d-%m-%Y", "timezone" : "+01", "date" : "$createdAt"}}}}, { "$sort" : { "isPinned" : -1, "createdAt" : -1}}, { "$project" : { "user" : 0, "_class" : 0}}]}
2020-01-12 16:15:22.161  INFO 11148 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:277}] to localhost:27017
{
  "username" : "user1",
  "title" : "postim2",
  "description" : "description2",
  "upvotesBy" : [ "5e18b4c12753608718dfa007", "5e19ac0f5161a4994ded1f35" ],
  "file" : "test2",
  "createdAt" : "10:46 12-01-2020",
  "id" : "5e1aeaf8275360bb4bb47325"
}
{
  "username" : "user1",
  "title" : "postim",
  "description" : "description",
  "upvotesBy" : [ "5e18b4c12753608718dfa007", "5e19ac0f5161a4994ded1f35" ],
  "file" : "test",
  "createdAt" : "10:44 12-01-2020",
  "id" : "5e1aea6c275360baf96bac29"
}
 类似资料:
  • 在ddd中,实体可以引用同一聚合的实体或另一聚合根(但不能引用另一聚合内的实体)。 如何实施这样的参考? 实体的方法如何访问引用的聚合根? 实体允许对另一个聚合根做的方法是什么? 对于1.和2.,我的问题是,实体不应该访问存储库。此外,神奇的延迟加载机制并不总是可用的,我认为出于同样的原因应该避免。因此,当存储库加载聚合时,存储库是否应该解析其中每个实体的所有引用(以及所有引用的其他聚合)?或者“

  • 我不熟悉Mongo中的聚合查询,并且一直在努力产生我想要的输出。我有以下聚合查询: 返回以下结果: 如何修改聚合查询,以便只返回2个文档而不是3个文档?将两个“ABC-123”结果合并为一个结果,并使用带有“bu”和“count”字段的新计数数组,即。 非常感谢

  • 代码中的聚合和关联是什么样子的?(Java或C#) 找不到聚合和关联的代码示例。

  • 问题内容: 我有一个非常庞大的查询,其最简单的形式如下所示: 我需要再添加一个条件,该条件可以让我获得每个代表的应用程序日期不为空的用户数(例如:rep 1具有3个用户的应用程序日期已填写),并将其分配给类别(由于3个用户,rep是某个状态类别)。看起来像这样: 但是,如果我只是将其添加到select语句中,则所有代表将变为status1,因为sum()是在所有使用申请日期的顾问程序上完成的: 您

  • 问题内容: 这是我第一次在Java中使用Mongo,并且此聚合查询存在一些问题。我可以在Mongo for Spring中执行一些简单的查询,并在我的Repository接口中扩展注解。知道在Spring-Data中进行长时间聚合时采用哪种方法会很有帮助。 问题答案: 您可以实现AggregationOperation 并编写自定义聚合操作查询,然后用于执行您在mongo shell中执行的任何m

  • 这是我第一次在Java中使用Mongo,这个聚合查询有一些问题。我可以在我的存储库界面中使用注释在Mongo for Spring中进行一些简单的查询,这扩展了