Can mongodb do this SQL? - mongodb

In MySQL Query:
UPDATE $TABLE SET `col1`=`col2` WHERE 1;
UPDATE $TABLE SET `col1`=IF(`col1`>100, `col1`-100, `col1`) WHERE 1;
Can MongoDB do like this?

What you appear to want to do is a conditional update - update the document based on its current state. MongoDB only has very limited support for this. See the update operators for details.
For the first query, it would be best to retrieve the documents, change them, and then save them. Unless your intention is not to copy the field but to rename it. Then you can use $rename.
For the second, it would be possible with this query:
db.collection.update(
{ field1: { $gt: 100} },
{ $inc: { field1, -100 },
{ multi: true}
);
By the way: You can also use the same pattern in SQL. It would likely work faster than using an if-condition.
UPDATE $TABLE SET `col1`=`col1`-100 WHERE `col1 ` > 100

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.

Using the value of a MongoDB field to update another field

I have a MongoDB document with a structure like this:
{
testValue: 10,
maxValue: 20
}
I want to set testValue to an arbitrary integer value between 0 and maxValue. Ideally, I would do something like this:
db.collection.update(
{},
{
$set: { 'testValue': newValue },
$min: { 'testValue': 0 },
$max: { 'textValue': $maxValue }
}
)
But that obviously doesn't work. There are a handful of threads that relate to this question (e.g. Update MongoDB field using value of another field), but they're all a few years old, and I can't find pertinent information in the official documentation. Is there a way to do what I want, or do I have to use find() to get maxValue then do a separate call to the database using update()?
This is still not possible in Mongo 3.2 or lower, check for example these JIRA tickets:
Self referential updates
Allow update to compute expressions using referenced fields

MongoDb - Equivalent for traditional update-set-where

Does mongo have an equivalent for
update emp
set sal = sal * 1.20
where empno in (1,2,3);
Note that I want the matched records sal.
db.users.update(
{ empno: { $in: [1,2,3]} },
{ $set: { sal: $matched.sal * 1.20 } }, # Not real syntax
{ multi: true }
)
I have looked through the documentation but couldn't find anything. I can do it with find-save but I am dealing with a large collection and multi update will be a more desirable solution.
In mongo shell there is no such function as multiply element. There are few field update operators, but in your situation you need to run a custom forEach script:
db.users.find({ empno: { $in: [1,2,3]} }).forEach(function(e) {
e.sal = e.sal * 1.2;
db.users.save(e);
});
Unfortunately, this is not possible with MongoDB. The closes you could get is with $where operator, but reference specifically warns against updating documents from $where. So, yes, you will have to fetch documents one by one and perform updates individually.

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

mongodb find by comparing field values

Is it possible to express the following SQL query in mongodb:
SELECT * FROM table AS t WHERE t.field1 > t.filed2;
edit:
To summarize:.
using a third field storing "field1 - field2" is almost perfect, but requires a little extra maintenance.
$where will load and eval in JavaScript and won't use any indexes. No good for large data.
map/reduce has the same problem and will go trough all records even if we need only one
You can do this using $where:
db.coll.find( { $where: "this.field1 > this.field2" } );
But:
Javascript executes more slowly than
the native operators, but it is very flexible
If performance is an issue better to go with way suggested by #yi_H.
You could store in your document field1 - field2 as field3, then search for { field3: { $gt: 0 } }
It also possible to get matching documents with mapreduce.
You can use a $where. Just be aware it will be fairly slow (has to execute Javascript code on every record) so combine with indexed queries if you can.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
or more compact:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );