使用 Spring Data Mongodb,是不是可以在不拉取和迭代整个集合的情况下获得字段的最大值?

Posted

技术标签:

【中文标题】使用 Spring Data Mongodb,是不是可以在不拉取和迭代整个集合的情况下获得字段的最大值?【英文标题】:Using Spring Data Mongodb, is it possible to get the max value of a field without pulling and iterating over an entire collection?使用 Spring Data Mongodb,是否可以在不拉取和迭代整个集合的情况下获得字段的最大值? 【发布时间】:2012-04-13 14:28:38 【问题描述】:

使用mongoTemplate.find(),我指定一个可以调用.limit().sort() 的查询:

.limit() 返回一个 Query 对象.sort() 返回一个 Sort 对象

鉴于此,我可以说 Query().limit(int).sort(),但这不会执行所需的操作,它只是对有限的结果集进行排序。

我也不能调用 Query().sort().limit(int) 因为 .sort() 返回一个 Sort()

那么使用 Spring Data,我如何在 mongoDB shell 中执行以下操作?也许有一种方法可以传递我还没有找到的原始查询?

如果需要,我可以扩展 Paging 界面......只是似乎没有任何帮助。谢谢!

> j =  order: 1 
 "order" : 1 
> k =  order: 2 
 "order" : 2 
> l =  order: 3 
 "order" : 3 
> db.test.save(j)
> db.test.save(k)
> db.test.save(l)
> db.test.find()
 "_id" : ObjectId("4f74d35b6f54e1f1c5850f19"), "order" : 1 
 "_id" : ObjectId("4f74d3606f54e1f1c5850f1a"), "order" : 2 
 "_id" : ObjectId("4f74d3666f54e1f1c5850f1b"), "order" : 3 
> db.test.find().sort( order : -1 ).limit(1)
 "_id" : ObjectId("4f74d3666f54e1f1c5850f1b"), "order" : 3 

【问题讨论】:

不幸的是,我认为您确实需要从 mongo 获取所有数据并手动执行此操作。 Using findOne in mongodb to get element with max id的可能重复 【参考方案1】:

通常,使用聚合 SQL 查询完成的事情可以(至少)在 NoSQL 存储中以三种方式处理:

使用 Map/Reduce。这有效地遍历所有记录,但更优化(适用于多线程和集群)。这是 MongoDB 的 map/reduce tutorial。

预先计算每个插入的最大值,并单独存储。因此,每当您插入一条记录时,都会将其与之前的最大值进行比较,如果更大,则更新数据库中的最大值。

获取内存中的所有内容并在代码中进行计算。这是最简单的解决方案。它可能适用于小型数据集。

选择其中一个取决于您对该最大值的使用情况。如果很少执行,例如一些角落报告,您可以使用 map/reduce。如果经常使用,则存储当前最大值。

【讨论】:

我对 mongo 还是很陌生,所以我还没有机会使用 map/reduce,但是我有一种直接的方法可以直接在 mongo 中完成此操作,如问题...除非外壳中可用的内容不适用于代码?有没有办法将那里可用的东西翻译成 Spring Data?在我看来,map/reduce 仍然需要完整获取集合,并且在您没有很多节点的情况下,并不会真正为您买太多。我的假设不正确吗?这仍然是我解决这个问题的最佳方法吗?【参考方案2】:

据我所知,Mongo 完全支持排序然后限制:请参阅 http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order

通过 map reduce 获取最大/最小值会非常慢,应该不惜一切代价避免。

我对 Spring Data 一无所知,但我可以推荐 Morphia 来帮助查询。否则,使用 Java 驱动程序的基本方法是:

DBCollection coll = db.getCollection("...");

DBCursor curr = coll.find(new BasicDBObject()).sort(new BasicDBObject("order", -1))
.limit(1);

if (cur.hasNext())
  System.out.println(cur.next());

【讨论】:

【参考方案3】:

使用聚合 $max 。 由于 $max 是仅在 $group 阶段可用的累加器运算符,因此您需要做一个技巧。 在组运算符中使用任何常量作为 _id 。 让我们仅以Mongodb site 中给出的示例为例--

考虑一个包含以下文档的sales集合:

 "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") 
 "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-02-03T09:00:00Z") 
 "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-03T09:05:00Z") 
 "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") 
 "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T09:05:00Z") 

如果您想找出所有商品中的最高价格。

db.sales.aggregate(
   [
     
       $group:
         
           _id: "1", //** This is the trick
           maxPrice:  $max: "$price" 
         
     
   ]
)

请注意“_id”的值 - 它是“1”。你可以放任何常数...

【讨论】:

【参考方案4】:

您可以在 sping-data-mongodb 中执行此操作。如果排序字段被索引(或@Id 字段),Mongo 将优化排序/限制组合。这会产生非常快的 O(logN) 或更好的结果。否则它仍然是 O(N) 而不是 O(N*logN) 因为它将使用 top-k 算法并避免全局排序 (mongodb sort doc)。这是来自 Mkyong's example 但我先进行排序并设置限制为一秒。

Query query = new Query();
query.with(new Sort(Sort.Direction.DESC, "idField"));
query.limit(1);
MyObject maxObject = mongoTemplate.findOne(query, MyObject.class);

【讨论】:

【参考方案5】:

由于第一个答案是正确的,但代码已过时,我正在回复一个对我有用的类似解决方案:

Query query = new Query();
query.with(Sort.by(Sort.Direction.DESC, "field"));
query.limit(1);
Entity maxEntity = mongoTemplate.findOne(query, Entity.class);

【讨论】:

以上是关于使用 Spring Data Mongodb,是不是可以在不拉取和迭代整个集合的情况下获得字段的最大值?的主要内容,如果未能解决你的问题,请参考以下文章

使用Spring访问Mongodb的方法大全——Spring Data MongoDB

用Spring data查询mongodb的问题,求解答

将 Spring 安全 ACL 与 spring-data-mongodb 一起使用

Spring @transactional 是不是与 MongoDB 一起使用?

Mongodb系列- 使用spring-data-mongodb实现分页查询

如何在 spring-boot 中禁用 spring-data-mongodb 自动配置