Delete array element having object key value mongoose - mongodb

Here is my collection:
{
"_id":"5b3385af20b7dc2b008ef5b9",
"name":"C",
"distances":[{"_id":"5b3460b05b2edc1bbcb0f362",
"distance":7,
"waypoint":"5b3385af20b7dc2b008ef5b9",
"status":"available"},
{"_id":"5b3460b05b2edc1bbcb0f361",
"distance":4,
"waypoint":"5b3460a15b2edc1bbcb0f360",
"status":"available"}],
"createdAt":"2018-06-27T12:40:15.457Z",
"updatedAt":"2018-06-27T12:57:50.191Z",
"__v":0
}
Let's focus on the distances array only, so which is:
"distances":[{"_id":"5b3460b05b2edc1bbcb0f362",
"distance":7,
"waypoint":"5b3385af20b7dc2b008ef5b9",
"status":"available"},
{"_id":"5b3460b05b2edc1bbcb0f361",
"distance":4,
"waypoint":"5b3460a15b2edc1bbcb0f360",
"status":"available"}]
What i want to do is, I want to delete object and update the distances array which have "waypoint":"5b3460a15b2edc1bbcb0f360"
so far I have tried:
Model.update( {'_id': model._id}, { $pullAll: {distances: [{'waypoint': req.body.id}] } });
this isn't working. Please suggest a way out.

You can use $pull of MongoDB
db.collection.update(
{ },
{ $pull: { distances: { waypoint: req.body.id} } },
)
{multi: true}: adding this in above query will delete all entries matching { waypoint: req.body.id}

Related

Delete entire document if array has no element after pull

Sample
db.collection.update({},
{
"$pull": {
"a": {
x: {
$lt: ISODate("2022-01-01T00:00:00Z")
}
}
}
},
{
multi: true
})
How do I delete the document if pull results empty array? I don't want to shoot two queries. Thought of using aggregate update using cond. But as per documentation, pull is not supported.
Any suggestions please?

Most efficient way to put fields of an embedded document in its parent for an entire MongoDB collection?

I am looking for the most efficient way to modify all the documents of a collection from this structure:
{
[...]
myValues:
{
a: "any",
b: "content",
c: "can be found here"
}
[...]
}
so it becomes this:
{
[...]
a: "any",
b: "content",
c: "can be found here"
[...]
}
Basically, I want everything under the field myValues to be put in its parent document for all the documents of a collection.
I have been looking for a way to do this in a single query using dbCollection.updateMany(), put it does not seem possible to do such thing, unless the content of myValues is the same for all documents. But in my case the content of myValues changes from one document to the other. For example, I tried:
db.getCollection('myCollection').updateMany({ myValues: { $exists: true } }, { $set: '$myValues' });
thinking it would perhaps resolve the myValues object and use that object to set it in the document. But it returns an error saying it's illegal to assign a string to the $set field.
So what would be the most efficient approach for what I am trying to do? Is there a way to update all the documents of the collection as I need in a single command?
Or do I need to iterate on each document of the collection, and update them one by one?
For now, I iterate on all documents with the following code:
var documents = await myCollection.find({ myValues: { $exists: true } });
for (var document = await documents.next(); document != null; document = await documents.next())
{
await myCollection.updateOne({ _id: document._id }, { $set: document.myValues, $unset: { myValues: 1} });
}
Since my collection is very large, it takes really long to execute.
You can consider using $out as an alternative, single-command solution. It can be used to replace existing collection with the output of an aggregation. Knowing that you can write following aggregation pipeline:
db.myCollection.aggregate([
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ "$$ROOT", "$myValues" ]
}
}
},
{
$project: {
myValues: 0
}
},
{
$out: "myCollection"
}
])
$replaceRoot allows you to promote an object which merges the old $$ROOT and myValues into root level.

How to add ObjectId property to each object of an array

I am have manually created an object in a Mongo collection:
{
"messages": [
{
"url":"http://test.test.com",
"message":"test message"
}
],
....other properties
}
I would like to add an _id:ObjectId() to each item of my messages array and for each document in the collection.
I tried:
collection.update({}, {
$set: {
'messages.$._id': ObjectId(),
},
}, { multi: true }
but this is not working. The Id is getting added when I add new ones going through Mongoose, but these were manually entered into mongo. Any help is appreciated.
Your syntax is correct, but in order to use the $ positional operator the array field must appear as part of the query document, check in documentation.
Try this:
db.collection.update({messages: {$exists: true }},
{$set: { 'messages.$._id': ObjectId() } },
{multi: true}
)

Deleting array from collection

I am facing an issue in deleting an array from db.
I want to delete the [2] array with its id as condition.
I have tried many things, but none give results as expected.
db.users.update({ '_id': ObjectId("53689fa45bac9757f81fbb77")},{ '$pull' : { 'injury._id': ObjectId("5379974ac76d005c2d00005c") } })
Use the $pull operator to remove the element you do not want:
db.collection.update(
{ "injury._id": ObjectId("53760d9820b6ee683000005c") },
{
"$pull": {
"injury": { "_id": ObjectId("53760d9820b6ee683000005c") }
}
}
)
Forgive me if the actual _id values do not match as you posted a screenshot.
I guess, something like this should work
.update(..., { $pull: { injury: {_id: YOUR_ID } } })
The format of $pull is
$pull: { arrayName: { array item query } }
You can use unset() if you're using PHP

How to limit number of updating documents in mongodb

How to implement somethings similar to db.collection.find().limit(10) but while updating documents?
Now I'm using something really crappy like getting documents with db.collection.find().limit() and then updating them.
In general I wanna to return given number of records and change one field in each of them.
Thanks.
You can use:
db.collection.find().limit(NUMBER_OF_ITEMS_YOU_WANT_TO_UPDATE).forEach(
function (e) {
e.fieldToChange = "blah";
....
db.collection.save(e);
}
);
(Credits for forEach code: MongoDB: Updating documents using data from the same document)
What this will do is only change the number of entries you specify. So if you want to add a field called "newField" with value 1 to only half of your entries inside "collection", for example, you can put in
db.collection.find().limit(db.collection.count() / 2).forEach(
function (e) {
e.newField = 1;
db.collection.save(e);
}
);
If you then want to make the other half also have "newField" but with value 2, you can do an update with the condition that newField doesn't exist:
db.collection.update( { newField : { $exists : false } }, { $set : { newField : 2 } }, {multi : true} );
Using forEach to individually update each document is slow. You can update the documents in bulk using
ids = db.collection.find(<condition>).limit(<limit>).map(
function(doc) {
return doc._id;
}
);
db.collection.updateMany({_id: {$in: ids}}, <update>})
The solutions that iterate over all objects then update them individually are very slow.
Retrieving them all then updating simultaneously using $in is more efficient.
ids = People.where(firstname: 'Pablo').limit(10000).only(:_id).to_a.map(&:id)
People.in(_id: ids).update_all(lastname: 'Cantero')
The query is written using Mongoid, but can be easily rewritten in Mongo Shell as well.
Unfortunately the workaround you have is the only way to do it AFAIK. There is a boolean flag multi which will either update all the matches (when true) or update the 1st match (when false).
As the answer states there is still no way to limit the number of documents to update (or delete) to a value > 1. A workaround to use something like:
db.collection.find(<condition>).limit(<limit>).forEach(function(doc){db.collection.update({_id:doc._id},{<your update>})})
If your id is a sequence number and not an ObjectId you can do this in a for loop:
let batchSize= 10;
for (let i = 0; i <= 1000000; i += batchSize) {
db.collection.update({$and :[{"_id": {$lte: i+batchSize}}, {"_id": {$gt: i}}]}),{<your update>})
}
let fetchStandby = await db.model.distinct("key",{});
fetchStandby = fetchStandby.slice(0, no_of_docs_to_be_updated)
let fetch = await db.model.updateMany({
key: { $in: fetchStandby }
}, {
$set:{"qc.status": "pending"}
})
I also recently wanted something like this. I think querying for a long list of _id just to update in an $in is perhaps slow too, so I tried to use an aggregation+merge
while (true) {
const record = db.records.findOne({ isArchived: false }, {_id: 1})
if (!record) {
print("No more records")
break
}
db.records.aggregate([
{ $match: { isArchived: false } },
{ $limit: 100 },
{
$project: {
_id: 1,
isArchived: {
$literal: true
},
updatedAt: {
$literal: new Date()
}
}
},
{
$merge: {
into: "records",
on: "_id",
whenMatched: "merge"
}
}
])
print("Done update")
}
But feel free to comment if this is better or worse that a bulk update with $in.