Delete an element from an array in mongodb - javascript

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

Related

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)

Insert documents into collection unless they are already inserted?

I get a list of objects from an API:
let sold = [
{ objId: 3240747,
soldDate: '2018-09-27',
soldPrice: 4610000,
apartmentNumber: '1202',
soldPriceSource: 'bid',
},
{ objId: 3234263,
soldDate: '2018-09-24',
soldPrice: 2580000,
soldPriceSource: 'bid',
}
...
]
I store these in a collection:
soldCollection.insertMany(sold)
Some of the objects have been retrieved before, and I only want to store the once that are not already in the database.
dbo.collection("sold").createIndex({ "objId": 1 }, { unique: true })
What would be an efficient way of doing this? Should I ask for each object before storing it or is there method for dealing with this?
By default insertMany will stop inserting when first error occurs (E11000 duplicate key error in this case). You can change that behavior by specifying ordered parameter set to false. In that case you'll get a list of errors from failed inserts however all valid documents will be inserted succesfully:
db.sold.insertMany(sold, { ordered: false })
docs example here

update array element mongodb query take too much time

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?

Mongoose: Add more items to existing object

Using Mongoose, How can I add more items to an object without replacing existing ones?
User.findOneAndUpdate(
{ userId: 0 },
{ userObjects: { newItem: value } }
);
The problem with above code is that it clears whatever was there before and replaces it with newItem when I wanted it just to add another item to userObjects(Like push function for javascript arrays).
Use dot notation to specify the field to update/add particular fields in an embedded document.
User.findOneAndUpdate(
{ userId: 0 },
{ "userObjects.newerItem": newervalue } }
);
or
User.findOneAndUpdate(
{ userId: 0 },
{ "$set":{"userObjects.newerItem": newervalue } }
);
or Use $mergeObjects aggregation operator to update the existing obj by passing new objects
User.findOneAndUpdate(
{"userId":0},
[{"$set":{
"userObjects":{
"$mergeObjects":[
"$userObjects",
{"newerItem":"newervalue","newestItem":"newestvalue"}
]
}
}}]
)
According to your question, i am guessing userObjects is an array.
You can try $push to insert items into the array.
User.findOneAndUpdate(
{ userId: 0 },
{ $push : {"userObjects": { newItem: value } }},
{safe :true , upsert : true},function(err,model)
{
...
});
For more info, read MongoDB $push reference.
Hope it helps you. If you had provided the schema, i could have helped better.
Just create new collection called UserObjects and do something like this.
UserObject.Insert({ userId: 0, newItem: value }, function(err,newObject){
});
Whenever you want to get these user objects from a user then you can do it using monogoose's query population to populate parent objects with related data in other collections. If not, then your best bet is to just make the userObjects an array.

MongoDB - $set to update or push Array element

In products collection, i have an Array of recentviews which has 2 fields viewedBy & viewedDate.
In a scenario if i already have a record with viewedby, then i need to update it. For e.g if i have array like this :-
"recentviews" : [
{
"viewedby" : "abc",
"vieweddate" : ISODate("2014-05-08T04:12:47.907Z")
}
]
And user is abc, so i need to update the above & if there is no record for abc i have to $push.
I have tried $set as follows :-
db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },
{ $set:
{ "recentviews":
{
viewedby: 'abc',
vieweddate: ISODate("2014-05-09T04:12:47.907Z")
}
}
}
)
The above query erases all my other elements in Array.
Actually doing what it seems like you say you are doing is not a singular operation, but I'll walk through the parts required in order to do this or otherwise cover other possible situations.
What you are looking for is in part the positional $ operator. You need part of your query to also "find" the element of the array you want.
db.products.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
"$set": {
"recentviews.$.vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
)
So the $ stands for the matched position in the array so the update portion knows which item in the array to update. You can access individual fields of the document in the array or just specify the whole document to update at that position.
db.products.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
"$set": {
"recentviews.$": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
)
If the fields do not in fact change and you just want to insert a new array element if the exact same one does not exist, then you can use $addToSet
db.products.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
$addToSet:{
"recentviews": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
)
However if you are just looking for for "pushing" to an array by a singular key value if that does not exist then you need to do some more manual handling, by first seeing if the element in the array exists and then making the $push statement where it does not.
You get some help from the mongoose methods in doing this by tracking the number of documents affected by the update:
Product.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
"$set": {
"recentviews.$": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
},
function(err,numAffected) {
if (numAffected == 0) {
// Document not updated so you can push onto the array
Product.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095")
},
{
"$push": {
"recentviews": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
},
function(err,numAffected) {
}
);
}
}
);
The only word of caution here is that there is a bit of an implementation change in the writeConcern messages from MongoDB 2.6 to earlier versions. Being unsure right now as to how the mongoose API actually implements the return of the numAffected argument in the callback the difference could mean something.
In prior versions, even if the data you sent in the initial update exactly matched an existing element and there was no real change required then the "modified" amount would be returned as 1 even though nothing was actually updated.
From MongoDB 2.6 the write concern response contains two parts. One part shows the modified document and the other shows the match. So while the match would be returned by the query portion matching an existing element, the actual modified document count would return as 0 if in fact there was no change required.
So depending on how the return number is actually implemented in mongoose, it might actually be safer to use the $addToSet operator on that inner update to make sure that if the reason for the zero affected documents was not just that the exact element already existed.

Categories

Resources