MongoDB聚合在Java中具有不同的价值?

Posted

技术标签:

【中文标题】MongoDB聚合在Java中具有不同的价值?【英文标题】:MongoDB aggregation wtih distinct value in Java? 【发布时间】:2021-12-05 14:35:53 【问题描述】:

我正在尝试根据唯一 ID 获取最新输入的文档。这是合集


        "_id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 8686868,
        "channelId" : "909090",
        "createdAt": 1


        "_id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 8686868,
        "channelId" : "909090"
        "createdAt": 2


        "_id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 10101010,
        "channelId" : "919191"
        "createdAt": 3

结果将是:


        "_id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 8686868,
        "channelId" : "909090"
        "createdAt": 2


        "_id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 10101010,
        "channelId" : "919191"
        "createdAt": 3

为元素提供独特的channelId 关键字和最新创建的channelId

我尝试了以下方法:

Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.sort(Sort.Direction.DESC, Constant.CREATED_AT),
            Aggregation.group(Constant.CHANNEL_ID));

return mongoTemplate.aggregate(aggregation, "tbl_message", Messages.class)
            .getMappedResults()
            .stream()
            .distinct()
            .collect(Collectors.toList());

上面的代码只是在整个集合中为我提供了不同的 channelId (s)。如何按要求更正上述说法?

【问题讨论】:

【参考方案1】:

您的示例文档无效,因为您在字段 _id 上存在唯一键违规。为此,我将该字段重命名为简单的id

样本测试数据

db.collection.insertMany([

        "id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 8686868,
        "channelId" : "909090",
        "createdAt": 1
,

        "id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 8686868,
        "channelId" : "909090",
        "createdAt": 2
,

        "id" : "535f5d074f075c37fff4cc74",
        "senderId" : 8989898,
        "receiverId" : 10101010,
        "channelId" : "919191",
        "createdAt": 3
])

除此之外,我认为您非常接近,但是您的 GROUP BY 子句需要一些更改。这是我认为可以工作的版本...

代码示例

package test.barry;

public class Main 
    public static void main(String[] args) 
        com.mongodb.client.MongoClient client = connectToReplicaSet();
        com.mongodb.client.MongoDatabase db = client.getDatabase("barrydb");
        com.mongodb.client.MongoCollection<org.bson.Document> collection = db.getCollection("collection");

        com.mongodb.client.AggregateIterable<org.bson.Document> iterable1 = collection.aggregate(
            java.util.Arrays.asList(
                com.mongodb.client.model.Aggregates.sort(com.mongodb.client.model.Sorts.orderBy(com.mongodb.client.model.Sorts.ascending("id"), com.mongodb.client.model.Sorts.ascending("channelId"), com.mongodb.client.model.Sorts.descending("createdAt"))),
                com.mongodb.client.model.Aggregates.group(
                    new org.bson.BsonDocument("id", new org.bson.BsonString("$id")).append("channelId", new org.bson.BsonString("$channelId")),
                        com.mongodb.client.model.Accumulators.first("senderId", "$senderId"),
                        com.mongodb.client.model.Accumulators.first("receiverId", "$receiverId"),
                        com.mongodb.client.model.Accumulators.first("createdAt", "$createdAt"),
                        com.mongodb.client.model.Accumulators.first("originalId", "$_id")
                ),
                com.mongodb.client.model.Aggregates.project(
                    com.mongodb.client.model.Projections.fields(
                        com.mongodb.client.model.Projections.computed("_id", "$originalId"),
                        com.mongodb.client.model.Projections.computed("id", "$_id.id"),
                        com.mongodb.client.model.Projections.include("senderId", "receiverId"),
                        com.mongodb.client.model.Projections.computed("channelId", "$_id.channelId"),
                        com.mongodb.client.model.Projections.include("createdAt")
                    )
                )
            )
        );

        // AUTO-CLOSABLE TRY
        try(com.mongodb.client.MongoCursor<org.bson.Document> cursor1 = iterable1.iterator())
        
            while (cursor1.hasNext())
            
                org.bson.Document queriedDocument1 = cursor1.next();
                System.out.println(String.format("queriedDocument1: %s", queriedDocument1));
            
        
    

    private static com.mongodb.client.MongoClient connectToReplicaSet() 
        java.util.ArrayList<com.mongodb.ServerAddress> hosts = new java.util.ArrayList<com.mongodb.ServerAddress>();
        hosts.add(new com.mongodb.ServerAddress("localhost", 50011));
        hosts.add(new com.mongodb.ServerAddress("localhost", 50012));
        hosts.add(new com.mongodb.ServerAddress("localhost", 50013));

        com.mongodb.MongoCredential mongoCredential = com.mongodb.MongoCredential.createScramSha256Credential("testuser", "admin", "mysecret".toCharArray());

        com.mongodb.MongoClientSettings mongoClientSettings = com.mongodb.MongoClientSettings.builder()
            .applyToClusterSettings(clusterSettingsBuilder -> clusterSettingsBuilder.hosts(hosts).requiredReplicaSetName("replSet"))
            .credential(mongoCredential)
            .writeConcern(com.mongodb.WriteConcern.MAJORITY)
            .readConcern(com.mongodb.ReadConcern.MAJORITY)
            .readPreference(com.mongodb.ReadPreference.primary())
            .retryWrites(true)
            .build();

        com.mongodb.client.MongoClient client = com.mongodb.client.MongoClients.create(mongoClientSettings);

        return client;
    

聚合解释

这个聚合有 3 个阶段

    排序 组 项目

$sort 阶段将按字段id 升序排序,然后按字段channelId 升序排序,然后按createdAt 降序排序。这会将记录按所需的顺序逻辑地组合在一起。

$group 阶段将由idchannelId 分组。因为文档已经在第一阶段排序,我们可以简单地取组的第一次出现并保留其他字段(通过累加器$first)。

分组阶段使文档的形状不太理想。要修复形状,请使用$project 将文档恢复为所需的形状。

示例输出

queriedDocument1: DocumentsenderId=8989898, receiverId=10101010, createdAt=3, _id=616d9387719033ca154188a2, id=535f5d074f075c37fff4cc74, channelId=919191
queriedDocument1: DocumentsenderId=8989898, receiverId=8686868, createdAt=2, _id=616d9387719033ca154188a1, id=535f5d074f075c37fff4cc74, channelId=909090

结论

看起来对最终输出没有额外的排序要求,因此 3 在 2 之前到达的自然顺序是可以的。如果需要,可以为最终输出应用额外的$sort 阶段。

【讨论】:

以上是关于MongoDB聚合在Java中具有不同的价值?的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB在具有附加字段的对象数组上聚合$lookup

MongoDB在具有附加字段的对象数组上聚合$lookup

在 MongoDB 中聚合双嵌套数组的文档

MongoDB聚合组和计数字符串

Mongodb匹配聚合不适用于日期

mongodb3 之单一用途聚合