update array element mongodb query take too much time - javascript

I have collection like this :
{
"_id" : ObjectId(),
"user_id":"10"
"movie_rate":[
{
rate:2,
movie_id:"120"
},
{
rate:null,
movie_id:"230"
},
]
}
I want to update movie rate array element with movie id and I build a query for doing this:
db.rates.update({
user_id: data.user_id,
"movie_rate.movie_id": data.movie_id
}, {
$set: {
"movie_rate.$.rate": data.rate
}
}
All movie id are unique, so there is just one element in the movie rate array that I wanted to update; however my update query take to much time to execute, I have 7000 document and each document have movie rate array which length is 3700.
I found out another solution which in the first place it seems to be very awful idea,I solve this problem in three query first I find document with find query and user id,then I loop over movie rate array and find out the index of that element, I wanted to update next I pull the element from array with movie id and at last I push document in the array with it's position that I found out in the find query my awful solution was significantly faster than the first one.
Here my pull query:
db.rates.update(
{
user_id: data.user_id
},
{
$pull: {
movie_rate: {
movie_id: data.movie_id
}
}
}
and here is my push query:
db.rates.update(
{
user_id: data.user_id
},
{
$push: {
movie_rate: {
$each: [{
'rate': data.rate,
'movie_id': data.movie_id
}],
$position: index
}
}
}
So why my second solution is faster than my first one?

Related

Performing a complex query in mongoDB

**Hi guys, I really need your help. In mongoDB, I have a users collection where each user has a "friends" array, consisting of other users' _id s.
I am finding a user, getting the array of that user's friends and trying to find every other user with the _id s that are in that friends array;
Then I'm trying to check if the found users have "notifications" array's length more than or equal to 50. If that's the case, I want to remove the first element from that array and then push a document in it, else I want to just push a document in the "notifications" array.
But it doesn't work, I am trying to do this:**
User.updateMany(
{
'_id': {
$in: usersFriends.map(userId => new ObjectId(userId))
}
},
{
$cond: [
{ $gte: [ {$size: ['$notifications']}, 50 ] },
{
$pop: {'$notifications': 1},
$push: {"$notifications": NOTIF}
},
{ $push: {"$notifications": NOTIF} }
],
}
)
.then(DATA => {
res.status(201).json({
data: data,
DATA: DATA
})
})
Please someone help me.

Delete an element from an array in mongodb

Suppose I have an object in mongoDB:
collection_name: book_info
{_id:"121as", "book_num":"12a", "book_name":"lotr", "info":[ {"borrowerAddress":["NY","Delhi"] },
{"borrowerAddress":["SI","Ghana"] } ] }
I want to delete an element "NY" from info.
For this I can do is fetch the object from book_info collection and apply loop and delete when element found inside array and save the data into db.
But I want to do is delete and update at same time without fetching data and looping and updating again.
Also, if direct db manipulation is possible, can anyone suggest me whether fetching data, applying loop and deleting data is efficient or deleting data from db directly is more faster, based on solutions provided.
If anyone needs any further information, please let me know.
Additional Question:
doc1: {_id:"121as", "book_num":"12a", "book_name":"lotr", "info":[ {"borrowerAddress":["NY","Delhi"] },
{"borrowerAddress":["SI","Ghana"] } ] }
doc2: {_id:"213s", "book_num":"1c", "book_name":"hp", "info":[ {"borrowerAddress":["NY","Delhi"] },
{"borrowerAddress":["SI","Ghana"] } ] }
Assume I have multiple documents inside a book_info collection with different book names and details and I want to delete document for "book_name":"lotr" only, I delete address "NY" from document.
You can try update query with $pull operator to remove matching element from array, $[] positional for all elements in info array,
db.collection.update(
{ "info.borrowerAddress": "NY" },
{ $pull: { "info.$[].borrowerAddress": "NY" },
{ multi: true }
})
Playground
Assume I have multiple documents inside a book_info collection with different book names and details and I want to delete document for "book_name":"lotr" only, I delete address "NY" from document.
db.collection.update(
{ "book_name":"lotr", "info.borrowerAddress": "NY" },
{ $pull: { "info.$[].borrowerAddress": "NY" },
{ multi: true }
})
Playground

mongoose mongodb - remove all where condition is true except one

If a collection have a list of dogs, and there is duplicate entries on some races. How do i remove all, but a single specific/non specific one, from just one query?
I guess it would be possible to get all from a Model.find(), loop through every index except the first one and call Model.remove(), but I would rather have the database handle the logic through the query. How would this be possible?
pseudocode example of what i want:
Model.remove({race:"pitbull"}).where(notFirstOne);
To remove all but one, you need a way to get all the filtered documents, group them by the identifier, create a list of ids for the group and remove a single id from
this list. Armed with this info, you can then run another operation to remove the documents with those ids. Essentially you will be running two queries.
The first query is an aggregate operation that aims to get the list of ids with the potentially nuking documents:
(async () => {
// Get the duplicate entries minus 1
const [doc, ...rest] = await Module.aggregate([
{ '$match': { 'race': 'pitbull'} },
{ '$group': {
'_id': '$race',
'ids': { '$push': '$_id' },
'id': { '$first': '$_id' }
} },
{ '$project': { 'idsToRemove': { '$setDifference': [ ['$id'], '$ids' ] } } }
]);
const { idsToRemove } = doc;
// Remove the duplicate documents
Module.remove({ '_id': { '$in': idsToRemove } })
})();
if purpose is to keep only one, in case of concurrent writes, may as well just write
Module.findOne({race:'pitbull'}).select('_id')
//bla
Module.remove({race:'pitbull', _id:{$ne:idReturned}})
If it is to keep the very first one, mongodb does not guarantee results will be sorted by increasing _id (natural order refers to disk)
see Does default find() implicitly sort by _id?
so instead
Module.find({race:'pitbull'}).sort({_id:1}).limit(1)

Mongoose/MongoDB: $in and .sort()

I hit an API which follows 50 members' data in a game once a day, and use mongoose to convert the JSON into individual documents in a collection. Between days there is data which is consistent, for example each member's tag (an id for the member in game), but there is data which is different (different scores etc.). Each document has a createdAt property.
I would like to find the most recent document for each member, and thus have an array with each member's tag.
I an currently using the following query to find all documents where tags match, however they are returning all documents, not just one. How do I sort/limit the documents to the most recent one, whilst keep it as one query (or is there a more "mongodb way")?
memberTags = [1,2,3,4,5];
ClanMember.find({
'tag': {
$in: memberTags
}
}).lean().exec(function(err, members) {
res.json(members);
});
Thanks
You can query via the aggregation framework. Your query would involve a pipeline that has stages that process the input documents to give you the desired result. In your case, the pipeline would have a $match phase which acts as a query for the initial filter. $match uses standard MongoDB queries thus you can still query using $in.
The next step would be to sort those filtered documents by the createdAt field. This is done using the $sort operator.
The preceding pipeline stage involves aggregating the ordered documents to return the top document for each group. The $group operator together with the $first accumulator are the operators which make this possible.
Putting this altogether you can run the following aggregate operation to get your desired result:
memberTags = [1,2,3,4,5];
ClanMember.aggregate([
{ "$match": { "tag": { "$in": memberTags } } },
{ "$sort": { "tag": 1, "createdAt: -1 " } },
{
"$group": {
"_id": "$tag",
"createdAt": { "$first": "$createdAt" } /*,
include other necessary fields as appropriate
using the $first operator e.g.
"otherField1": { "$first": "$otherField1" },
"otherField2": { "$first": "$otherField2" },
...
*/
}
}
]).exec(function(err, members) {
res.json(members);
});
Or tweak your current query using find() so that you can sort on two fields, i.e. the tag (ascending) and createdAt (descending) attributes. You can then select the top 5 documents using limit, something like the following:
memberTags = [1,2,3,4,5];
ClanMember.find(
{ 'tag': { $in: memberTags } }, // query
{}, // projection
{ // options
sort: { 'createdAt': -1, 'tag': 1 },
limit: memberTags.length,
skip: 0
}
).lean().exec(function(err, members) {
res.json(members);
});
or
memberTags = [1,2,3,4,5];
ClanMember.find({
'tag': {
$in: memberTags
}
}).sort('-createdAt tag')
.limit(memberTags.length)
.lean()
.exec(function(err, members) {
res.json(members);
});
Ok, so, first, let's use findOne() so you get only one document out of the request
Then to sort by the newest document, you can use .sort({elementYouWantToSort: -1}) (-1 meaning you want to sort from newest to oldest, and 1 from the oldest to the newest)
I would recommend to use this function on the _id, which already includes creation date of the document
Which gives us the following request :
ClanMember.findOne({
'tag': {
$in: memberTags
}
}).sort({_id: -1}).lean().exec(function(err, members) {
res.json(members);
});

Meteor Calculated Value in Collection Field

Is there a way to have a allways calculated value in Meteor collection field? I am currently developing an app to manage inventory of sandwiches. Each sandwich can depend on ingredients in other collections. I need to have a field always auto calculated to the number of the ingredient that is lowest in stock. How can i achieve this? I can not find anything about this when I Google, is it possible that Meteor does not have any support for this?
This sounds like a job for a collection hook. Collection hooks allow you to execute an action before/after collections are inserted/updated/etc.
Let's say you have an ingredients collection. Perhaps that ingredients collection has a schema like:
Ingredients = new Mongo.Collection('ingredients');
IngredientsSchema = new SimpleSchema({
"name": {
type: String
},
"quantity": {
type: Number
}
});
Ingredients.attachSchema(IngredientsSchema);
Then you have a sandwiches collection with a hypothetical schema:
Sandwiches = new Mongo.Collection('sandwiches');
SandwichesSchema = new SimpleSchema({
"name": {
type: String
},
"ingredients": {
type: [String],
label: "An array of ingredient internal ids (_id)"
},
"quantity": {
type: Number
}
});
Sandwiches.attachSchema(SandwichesSchema);
Your collection hook would be something along the lines of:
Ingredients.after.update(function(userId, doc, fieldNames, modifier, options) {
// Find the ingredient with the lowest value
ingredient = Ingredients.findOne({}, { sort: { quantity: 1 } });
if(ingredient && ingredient._id == doc._id) {
//If the ingredient matches this ingredient, update all sandwiches who have the agreement to reflect the remaining quantity of ingredients.
Sandwiches.update({ ingredients: doc._id }, { $set: { quantity: doc.quantity } }, { multi: true });
}
});
You'll probably also need a collection hook after inserting an ingredient, but this should be plenty to get you started.

Categories

Resources