IN condition is not working in mongoDB - mongodb

I want to delete some collections from MongoDB based on some conditions.
I am using { $in: deleteModule}, where deletemodule is an array ["ab","bc"].
But its deleting only the record which is existing first in the collection.
myDb.collection('ABC').findAndModify(
{'projectName': projectName, 'companyName': companyName,'moduleName':{ $in: deleteModule}},
[['_id', 'asc']],
{ 'remove': true },

findAndModify can be used to atomically modify a document (at most one) and return it. It will remove only the first matched document. This is how it is implemented.
The official document says so: https://docs.mongodb.com/manual/reference/command/findAndModify/
Use remove for your use case.
Example:
db.users.remove({'_id':{'$in':inactive_users}})

You need to use $in condition to achieve the result
ABC.remove({ moduleName: { $in: deleteModule } })

Related

how can I make the "updated" of mongodb stop when updating a field of a nested array?

I have a database like this:
{
"universe":"comics",
"saga":[
{
"name":"x-men",
"characters":[
{
"character":"wolverine",
"picture":"618035022351.png"
},
{
"character":"wolverine",
"picture":"618035022352.png"
}
]
}
]
},
{
"universe":"dc",
"saga":[
{
"name":"spiderman",
"characters":[
{
"character":"venom",
"picture":"618035022353.png"
}
]
}
]
}
And with this code, I update the field where name: wolverine:
db.getCollection('collection').findOneAndUpdate(
{
"universe": "comics"
},
{
$set: {
"saga.$[outer].characters.$[inner].character": "lobezno",
"saga.$[outer].characters.$[inner].picture": "618035022354.png"
}
},
/*{
"saga.characters": 1
},*/
{
"arrayFilters": [
{
"outer.name": "x-men"
},
{
"inner.character": "wolverine"
}
],
"multi":false
}
)
I want to just update the first object where there is a match, and stop it.
For example, if I have an array of 100,000 elements and the object where the match is, is in the tenth position, he will update that record, but he will continue going through the entire array and this seems ineffective to me even though he already did the update.
Note: if I did the update using an _id inside of universe.saga.characters instead of doing the update using the name, it would still loop through the rest of the elements.
How can I do it?
Update using arrayFilters conditions
I don't think it will find and update through loop, and It does not matter if collection have 100,000 sub documents, because here is nice explanation in $[<identifier>] and has mentioned:
The $[<identifier>] to define an identifier to update only those array elements that match the corresponding filter document in the arrayFilters
In the update document, use the $[<identifier>] filtered positional operator to define an identifier, which you then reference in the array filter documents. But make sure you cannot have an array filter document for an identifier if the identifier is not included in the update document.
Update using _id
Your point,
Note: if I did the update using an _id inside of universe.saga.characters instead of doing the update using the name, it would still loop through the rest of the elements.
MongoDB will certainly use the _id index. Here is the nice answer on question MongoDB Update Query Performance, from this you will get an better idea on above point
Update using indexed fields
You can create index according to your query section of update command, Here MongoDB Indexes and Indexing Strategies has explained why index is important,
In your example, lets see with examples:
Example 1: If document have 2 sub documents and when you update and check with explain("executionStats"), assume it will take 1 second to update,
quick use Mongo Playground (this platform will not support update query)
Example 2: If document have 1000 sub documents and when you update and check with explain("executionStats"), might be it will take more then 1 second,
If provide index on fields (universe, saga.characters.character and saga.characters.picture) then definitely it will take less time then usual without index, main benefit of index it will direct point to indexed fields.
quick use Mongo Playground (this platform will not support update query)
Create Index for your fields
db.maxData.createIndex({
"universe": 1,
"saga.characters.character": 1,
"saga.characters.picture": 1
})
For more experiment use above 2 examples data with index and without index and check executionStats you will get more clarity.

Use $not or $ne in update query

Should I use $not or $ne in the query:
Mytable.update({ TheThing: Thing,
'UpdatedInfo.NewInfo': {$ne: ThisNewestInfo} }, {
$push: {
UpdatedInfo: {
TheDate: ThisDate,
NewInfo: ThisNewestInfo,
Past: OriginalInfo
}
}
},
function (err, result) {
if (err){
throw new Error(err.message);
}
}
If I only want to update the document when ThisNewestInfo is not already present in UpdatedInfo array, in NewInfo object element. Trying to understand the difference between $not and $ne.
And also:
If the document does not contain UpdatedInfofield in the beginning? How should I change the update query above? Meaning that if UpdatedInfodoes not exists it adds UpdatedInfo, and later on, say next day, checks if ThisNewestInfois not already present when updating document again.
It depends on your collection actually.
The main different between $ne and $not in this scenario is that, $not performs a logical disjunction. That is if your document didn't had an UpdatedInfo field, using $not would have pushed the document while using $ne nothing would have happened to that document.
So if all your document of collection has UpdatedInfo field, its better to go with $ne.
Edit
Based on your edit you mentioned UpdatedInfo might not be present in document. In such cases you should use $not. $ne wont be able to update docs that doesn't have UpdatedInfo field.
Remember like this: $not checks for presence of key as well as value, while $ne checks only for value and ignores document that doesn't have the particular key in query.

Add a new field point type filed to a collection with value of an existing field

I have a huge mongodb collection with 6 million records. I have two fields (latitude, longitude), and I would like to add a third field to the collection with the type of point (spatial). How to do this in command line or PHP?
It you'd like to add a new field (with the same value) to all documents in a collection, that can be done easily with an update() operation. Consider the following shell example:
db.collection.update(
{},
{ $set: { type: "spatial" }},
{ multi: true }
);
This would set the type field to "spatial" for all documents matching empty criteria {} (i.e. everything), and the multi option allows the update to modify multiple documents instead of just the first document matched (default behavior).
If you only wanted to set the type field where it doesn't already exist, you could tweak the criteria like so:
db.collection.update(
{ type: { $exists: false }},
{ $set: { type: "spatial" }},
{ multi: true }
);
Since you're storing geospatial data, you may want to have a look at MongoDB's 2dsphere indexes. This would allow you to store and index well-formed GeoJSON objects in your document. See this previous answer from a related question for more introductory information on the subject.

mongodb: upserting: only set value if document is being inserted

Considering a simple mongo document structure:
{ _id, firstTime, lastTime }
The client needs to insert a document with a known ID, or update an existing document. The 'lastTime' should always be set to some latest time. For the 'firstTime', if a document is being inserted, then the 'firstTime' should be set to current time. However, if the document is already created, then 'firstTime' remain unchanged. I would like to do it purely with upserts (to avoid look ups).
I've crawled the http://www.mongodb.org/display/DOCS/Updating, but I just don't see how that particular operation can be done.
I don't believe this is something unreasonable, there are $push and $addToSet operations that effectively do that on array fields, just nothing that would do the same on simple fields. It's like there should be something like $setIf operation.
I ran into the exact same problem and there was no simple solution for <2.4 however since 2.4 the $setOnInsert operator let's you do exactly that.
db.collection.update( <query>,
{ $setOnInsert: { "firstTime": <TIMESTAMP> } },
{ upsert: true }
)
See the 2.4 release notes of setOnInsert for more info.
I ran into a very similar problem when attempting to upsert documents based on existing content--maybe this solution will work for you also:
Try removing the _id attribute from your record and only use it in the query portion of your update (you'll have to translate from pymongo speak...)
myid = doc.get('_id')
del doc['_id']
mycollection.update({'_id':myid}, {'$set':doc}, upsert=True)
If you will trigger the following code 2 subsequent times, it will first set both firstVisit and lastVisit on document insert (and will return upsertedId in the response) and on the second it will only update lastVisit (and will return modifiedCount: 1).
Tested with Mongo 4.0.5 though I believe should be working with older versions.
db.collection.updateOne(
{_id: 1},
{
$set: {
lastVisit: Date.now()
},
$setOnInsert: {
firstVisit: Date.now()
}
},
{ upsert: true }
);
There's no way to do this with just one upsert. You'd have to do it as 2 operations - first try to insert the document, if it already exists the insert will fail due to duplicate key violation on the _id index. Then you do an update operation to set the lastTime to now.

MongoDB: update every document on one field

I have a collected named foo hypothetically.
Each instance of foo has a field called lastLookedAt which is a UNIX timestamp since epoch. I'd like to be able to go through the MongoDB client and set that timestamp for all existing documents (about 20,000 of them) to the current timestamp.
What's the best way of handling this?
Regardless of the version, for your example, the <update> is:
{ $set: { lastLookedAt: Date.now() / 1000 } }
However, depending on your version of MongoDB, the query will look different. Regardless of version, the key is that the empty condition {} will match any document. In the Mongo shell, or with any MongoDB client:
$version >= 3.2:
db.foo.updateMany( {}, <update> )
{} is the condition (the empty condition matches any document)
3.2 > $version >= 2.2:
db.foo.update( {}, <update>, { multi: true } )
{} is the condition (the empty condition matches any document)
{multi: true} is the "update multiple documents" option
$version < 2.2:
db.foo.update( {}, <update>, false, true )
{} is the condition (the empty condition matches any document)
false is for the "upsert" parameter
true is for the "multi" parameter (update multiple records)
This code will be helpful for you
Model.update({
'type': "newuser"
}, {
$set: {
email: "abc#gmail.com",
phoneNumber:"0123456789"
}
}, {
multi: true
},
function(err, result) {
console.log(result);
console.log(err);
})
I have been using MongoDB .NET driver for a little over a month now. If I were to do it using .NET driver, I would use Update method on the collection object. First, I will construct a query that will get me all the documents I am interested in and do an Update on the fields I want to change. Update in Mongo only affects the first document and to update all documents resulting from the query one needs to use 'Multi' update flag. Sample code follows...
var collection = db.GetCollection("Foo");
var query = Query.GTE("No", 1); // need to construct in such a way that it will give all 20K //docs.
var update = Update.Set("timestamp", datetime.UtcNow);
collection.Update(query, update, UpdateFlags.Multi);
You can use updateMany() methods of mongodb to update multiple document
Simple query is like this
db.collection.updateMany(filter, update, options)
For more doc of uppdateMany read here
As per your requirement the update code will be like this:
User.updateMany({"created": false}, {"$set":{"created": true}});
here you need to use $set because you just want to change created from true to false. For ref. If you want to change entire doc then you don't need to use $set