Saturday, July 1, 2017

Sort by array's last element mongodb

Leave a Comment

I was trying to sort documents by last interaction. meta_data.access_times is an array that update every time when user interacts and new date object append to the last element of the array. Is there any way to sort by array's last element?

Attempt 1 :

private Aggregation makeQuery(String userId) {      return newAggregation(           match(Criteria.where("user_id").is(userId)),           sort(Sort.Direction.DESC, "$meta_data.access_times"),           group(Fields.fields().and("first_name", "$meta_data.user_data.first_name").and("last_name", "$meta_data.user_data.last_name").and("profile_pic", "$meta_data.user_data.profile_pic").and("user_id", "$user_id").and("access_times", "$meta_data.access_times"))       );     } 

Attempt 2 :

 private Aggregation makeQuery(String userId) {         return newAggregation(             match(Criteria.where("user_id").is(user_id)),             group(Fields.fields().and("first_name", "$meta_data.user_data.first_name").and("last_name", "$meta_data.user_data.last_name").and("profile_pic", "$meta_data.user_data.profile_pic").and("user_id", "$user_id")).max("$meta_data.access_times").as("access_time"),             sort(Sort.Direction.DESC, "access_time")         );     } 

sample meta_data array in document

"meta_data" : { "access_times" : [              ISODate("2017-06-20T14:04:14.910Z"),              ISODate("2017-06-22T06:27:32.210Z"),              ISODate("2017-06-22T06:27:35.326Z"),              ISODate("2017-06-22T06:31:28.048Z"),              ISODate("2017-06-22T06:36:19.664Z"),              ISODate("2017-06-22T06:37:00.164Z")         ] } 

2 Answers

Answers 1

I solves the problem by using $unwind operation.

 private Aggregation makeQuery(String userId) {         return newAggregation(             match(Criteria.where("user_id").is(userId)),             unwind("$meta_data.access_times"),             group(Fields.fields().and("first_name", "$meta_data.user_data.first_name").and("last_name", "$meta_data.user_data.last_name").and("profile_pic", "$meta_data.user_data.profile_pic").and("user_id", "$user_id")).max("$meta_data.access_times").as("access_time"),             sort(Sort.Direction.DESC, "access_time")         );     } 

Answers 2

When you don't know if the element to Push is Ordered or not (for example, an User that is pushing him Score...) you can use $push and $sort in order to have an ordered array, then you can just sort by "find({userId:yourUseId}.sort("metadata.access_time.0":-1).

This solution suppose your array are Ordered with $sort at creation/update time: LINK

When you are sure that the Push don't need a sort (for example you are Pushing a Access_Date for that User) you can $push and void $sort by using $operator (tnx Erdenezul). LINK

In theory you don't need an Index on the Array "access_time" if the find() is fetching only fews documents. Otherwise you can just add an index with {"metadata.access_time.0": -1}.

Good Luck!

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment