Sort by array's last element mongodb - arrays

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")
] }

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")
);
}

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!

Related

Swift array filter with empty filter value

I am filtering an array based on a set filter supplied to the view (this filter varies) but want to modify the function to allow for an empty filter, i.e return the original array un filtered.
I feel like this should be a simple matter but I'm currently a bit lost - have looked at the .isEmpty modifier and a couple of different IF statements but I'm not "getting it"
var categoryFilter:String
var filteredCategoryTasks: [Task] {
modelData.tasks.filter { task in
(task.category == categoryFilter)
}
}
You could return the original array in the event that the String is empty and the filtered array otherwise:
var filteredCategoryTasks: [Task] {
categoryFilter.isEmpty ? modelData.tasks : modelData.tasks.filter { task in
(task.category == categoryFilter)
}
}

MongoDB - Only fetch the first item in an array on a populated document - e.g. user.populate('posts', 'images.0')

Say I'm fetching a user with a posts field. Each post has a field images. I want to populate the posts on the user, but only fetch the first image from each post. How can I do this? I've given the following as an example that does not work:
db.User.findOne().populate('posts', 'images.0')
You can use $slice projection operator to specifies the number of elements in an array to return in the query result.
db.User.findOne().populate('posts', { 'images': { '$slice': 1 } })
If you want to select direct string instead of array of string use $arrayElemAt or $first aggregation operator Starting from MongoDB 4.4,
$arrayElemAt
db.User.findOne().populate('posts', { 'images': { '$arrayElemAt': ["$images", 0] } })
$first
db.User.findOne().populate('posts', { 'images': { '$first': "$images" } })

how do i access elements of an array in laravel?

I am trying to count the number of elements that are equal to a certain value but im struggling to get to the element that i want to compare, when i run:
{{dd($numberofnotifications)}}
I get the following:
But the value i need to compare is under 'attributes'
so how do i get to the values under attributes?
When i print out each element of the array i get the following format:
{"id":"96a40ebb-a2d1-44d8-9600-e94dd026f152","type":"App\\Notifications\\CommentCreated","notifiable_type":"App\\User","notifiable_id":1,"data":{"comment_id":9,"data":{"name":"John","date":"2020-12-30T08:37:47.000000Z","email":"John#gmail.com","post":2,"body":"test"},"message":"John commented on your post"},"read_at":null,"created_at":"2020-12-30T08:37:47.000000Z","updated_at":"2020-12-30T08:37:47.000000Z"}
It seems that the items is an array of DatabaseNotifications. So, you can get each element manually by its index:
$numberofnotifications[0]->id;
// or
$numberofnotifications[0]->type
or you can use foreach loop:
foreach ($numberofnotifications as $notification) {
$notification->id;
}
As the result is a collection, you can convert them using
$numberofnotifications->toArray();
you'll need to use foreach to iterate through the collection:
foreach($numberofnotification as $number)
{
$number->name; //name here is the "name" of the attribute you want to access
}
if you want to store them in an array then:
foreach($numberofnotification as $number)
{
$attributes[]=$number->name; //name here is the "name" of the attribute you want to access
}
Did you try this
//consider that $username is the logged in user name
$filtered_count = $numberofnotifications->where('name', $username)->count();
and if you want the record
$filtered = $numberofnotifications->where('name', $username)->all();

Mongo DB: Sorting by the number of matches

I have an array of objects, and I want to query in a MongoDB collection for documents that have elements that match any objects in my array of objects.
For example:
var objects = ["52d58496e0dca1c710d9bfdd", "52d58da5e0dca1c710d9bfde", "52d91cd69188818e3964917b"];
db.scook.recipes.find({products: { $in: objects }}
However, I want to know if I can sort the results by the number of matches in MongoDB.
For example, at the top will be the "recipe" that has exactly three elements matches: ["52d58496e0dca1c710d9bfdd", "52d58da5e0dca1c710d9bfde", "52d91cd69188818e3964917b"].
The second selected has two recipes: i.e. ["52d58496e0dca1c710d9bfdd", "52d58da5e0dca1c710d9bfde"], and the third one only one: i.e. ["52d58496e0dca1c710d9bfdd"]
It would be great if you could get the number of items it had.
By using the aggregation framework, I think that you should be able to get what you need by the following MongoDB query. However, if you're using Mongoose, you'll have to convert this to a Mongoose query. I'm not certain this will work exactly as is, so you may need to play with it a little to make it right. Also, this answer hinges on whether or not you can use the $or operator inside of the $project operator and that it will return true. If that doesn't work, I think you'll need to use map-reduce to get what you need or do it server side.
db.recipes.aggregate(
// look for matches
{ $match : { products : { $or : objects }}},
// break apart documents to by the products subdocuments
{ $unwind : "$products" },
// search for matches in the sub documents and add productMatch if a match is found
{ $project : {
desiredField1 : 1,
desiredField2 : 1,
products : 1,
// this may not be a valid comparison, but should hopefully
// be true or 1 if there is a match
productMatch : { "$products" : { $or : objects }}
}},
// group the unwound documents back together by _id
{ $group : {
_id : "$_id",
products : { $push : "$products" },
// count the matched objects
numMatches : { $sum : "$productMatch" },
// increment by 1 for each product
numProducts : { $sum : 1 }
}},
// sort by descending order by numMatches
{ $sort : { numMatches : -1 }}
)

Mongodb - Update multiple array element inside JSON array

I have an collection doc like .
{'_id':1,
'name':'Root',
'taskId':1,
'parentId':"",
'path':[1],
'tasks':[ {"taskId":3,parentId:1,name:'A',status:'Created'},
{"taskId":4,parentId:1,name:'D',status:'Created'},
{"taskId":5,parentId:4,name:'B',status:'Created'},
{'type':'project' , 'proRef':2},
{"taskId":6,parentId:3,name:'E',status:'Started'},
{"taskId":7,parentId:6,name:'C',status:'Stopped'}]
}
Now I want to update multiple array element field ‘status’ to ‘Deleted’ which is inside JSON .Let us assume for taskId 3,4 I need to update status to Deleted . I have tried this query with $in with query($) but it is updating very first element is $in array only. In below query only taskId with 3 is getting updated not 4.
db.projectPlan.update({"_id": 1,'tasks.taskId': {$in :[3,4]}} , {$set: {'tasks.$.status': 'Deleted'}}, {upsert: false, multi: true});
How to update multiple elements in single query.Thanks in advance !!
I'm afraid it's not possible, it's a limitation of MongoDB. From the documentation (http://docs.mongodb.org/manual/reference/operator/update-array/):
$ Acts as a placeholder to update the first element that matches the
query condition in an update.
See this ticket for more details: https://jira.mongodb.org/browse/SERVER-1243
It's possible though in MongoDB shell:
db.projectPlan.find({"_id": 1}).forEach(function(plan) {
plan.tasks.forEach(function(task) {
if(task.taskId in { 3: 1, 4: 1 }) {
task.status = "Deleted";
}
});
db.projectPlan.save(plan);
});

Resources