How to implement aggregation query in Spring Data MongoDB?(如何在Spring Data MongoDB中实现聚合查询?)
问题描述
我是Spring data MongoDB的新手,我正在尝试用Spring data MongoDB用Java实现聚合查询。我已尝试从此问题进行搜索,并使用MongoTemplate解决了它,但仍未找到结果。
我的数据格式:
[{ 
    "_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 } }
])
这是我在我的Java后端中使用的查询,我可以用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("username")时,我也从.push("upvotesBy").as("upvotesBy")收到错误,因为它无法转换[] of type class java.util.ArrayList into an instance of class 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
}
我还需要实现查询的isUpvoted和isPinned部分,但了解如何处理第一个问题将是一个很好的开始。
编辑:我想要的输出:
[
{
   "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
}
]
因此,从查找操作中,我只需要获取用户名。
推荐答案
开始吧
我们需要更新您的聚合才能使其工作。
错误:
users的_id为ObjectId类型,但您在帖子中存储为String,因此$lookup应改为Uncorrelated sub-queries- 我们将
$group替换为更适合的‘$addFields’ - 我们添加作为最后阶段的
$project运算符,以排除所有未使用的字段。 
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-data语法。
Java实现
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"
}
                        这篇关于如何在Spring Data MongoDB中实现聚合查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何在Spring Data MongoDB中实现聚合查询?
				
        
 
            
        - 获取数字的最后一位 2022-01-01
 - 未找到/usr/local/lib 中的库 2022-01-01
 - 在 Java 中,如何将 String 转换为 char 或将 char 转换 2022-01-01
 - Eclipse 的最佳 XML 编辑器 2022-01-01
 - 将 Java Swing 桌面应用程序国际化的最佳实践是什么? 2022-01-01
 - GC_FOR_ALLOC 是否更“严重"?在调查内存使用情况时? 2022-01-01
 - java.lang.IllegalStateException:Bean 名称“类别"的 BindingResult 和普通目标对象都不能用作请求属性 2022-01-01
 - 如何指定 CORS 的响应标头? 2022-01-01
 - 转换 ldap 日期 2022-01-01
 - 如何使 JFrame 背景和 JPanel 透明且仅显示图像 2022-01-01
 
						
						
						
						
						
				
				
				
				