Using Spring Data Mongodb, is it possible to get the max value of a field without pulling and iterating over an entire collection?

只谈情不闲聊 提交于 2019-12-10 13:57:34

问题


Using mongoTemplate.find(), I specify a Query with which I can call .limit() or .sort():

.limit() returns a Query object
.sort() returns a Sort object

Given this, I can say Query().limit(int).sort(), but this does not perform the desired operation, it merely sorts a limited result set.

I cannot call Query().sort().limit(int) either since .sort() returns a Sort()

So using Spring Data, how do I perform the following as shown in the mongoDB shell? Maybe there's a way to pass a raw query that I haven't found yet?

I would be ok with extending the Paging interface if need be...just doesn't seem to help any. Thanks!

> 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 }

回答1:


You can do this in sping-data-mongodb. Mongo will optimize sort/limit combinations IF the sort field is indexed (or the @Id field). This produces very fast O(logN) or better results. Otherwise it is still O(N) as opposed to O(N*logN) because it will use a top-k algorithm and avoid the global sort (mongodb sort doc). This is from Mkyong's example but I do the sort first and set the limit to one second.

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



回答2:


Normally, things that are done with aggregate SQL queries, can be approached in (at least) three ways in NoSQL stores:

  • with Map/Reduce. This is effectively going through all the records, but more optimized (works with multiple threads, and in clusters). Here's the map/reduce tutorial for MongoDB.

  • pre-calculate the max value on each insert, and store it separately. So, whenever you insert a record, you compare it to the previous max value, and if it's greater - update the max value in the db.

  • fetch everything in memory and do the calculation in the code. That's the most trivial solution. It would probably work well for small data sets.

Choosing one over the other depends on your usage of this max value. If it is performed rarely, for example for some corner reporting, you can go with the map/reduce. If it is used often, then store the current max.




回答3:


As far as I am aware Mongo totally supports sort then limit: see http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order

Get the max/min via map reduce is going to be very slow and should be avoided at all costs.

I don't know anything about Spring Data, but I can recommend Morphia to help with queries. Otherwise a basic way with the Java driver would be:

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());



回答4:


Use aggregation $max . As $max is an accumulator operator available only in the $group stage, you need to do a trick. In the group operator use any constant as _id .
Lets take the example given in Mongodb site only --

Consider a sales collection with the following documents:

{ "_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") }

If you want to find out the max price among all the items.

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

Please note that the value of "_id" - it is "1". You can put any constant...



来源:https://stackoverflow.com/questions/9933904/using-spring-data-mongodb-is-it-possible-to-get-the-max-value-of-a-field-withou

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!