This question already has answers here:
MongoDb query condition on comparing 2 fields
(4 answers)
Closed 4 years ago.
I'm trying to query a huge mongo collection which have around 50 + Million records. In the mongo query , I only need few fields. Object ID and MD5 which is present in the document. For that , I did
Query :
db.getCollection('experimental_engine').find({},{"md5":1,"_id":1})
Result :
/* 1 */
{
"_id" : "5cee41f2ca4e0ebf567ffd1be5cdaf1f",
"md5" : "1d813cb29082b13efe572e8088f006dd"
}
/* 2 */
{
"_id" : "fcd79aac0d5c5ebdfd0fa389368ab6f3",
"md5" : "13a1a6cd5c8f1c5eaf3d409f4d809889"
}
/* 3 */
{
"_id" : "2a0b42d01892bd9b7368d045a4c7862c",
"md5" : "2a0b42d01892bd9b7368d045a4c7862c"
}
................
Now , i wanted to match both "_id" and "md5" and get only matching values ( _id = md5 ) .
Do mongo command support match values for two keys ?
Any suggestion please ?
You can use $expr which allows the use of aggregation expressions within the query language.
db.collection.find({ "$expr": { "$eq": [ "$_id" , "$md5" ] } })
or with aggregation
db.collection.aggregate([
{ "$match": { "$expr": { "$eq": [ "$_id" , "$md5" ] } } }
])
Related
I'm fairly new to mongodb so please bear with me.
As per title, what I want to achieve is to convert a specific field in all documents within an array of a document from String to Int how do i do that?
Sample Doc :
{
reviews:[
{
snid:"1242"
},
{
snid:"8392"
}
]
}
And my objective is to convert all of the snid's from String to Int32
so far i understand that we can use something like db.collection.update() but this will update a specific field, not an array.
Another attempt is
db.collection.find({},{reviews:1,_id:0},(err,doc)=>{
//How do i push it back to the document
})
But as you can tell, I'm not entirely sure on how we should push the updated document back into the same array of sorts.
Any insights will be greatly appreciated!
1) If you're using MongoDB version >= 4.2, try below query :
db.collection.update({'reviews.snid' : {$exists : true}}, [
{
$set: {
reviews: {
$map: { input: "$reviews", in: { 'snid': { $toInt: "$$this.snid" } } }
}
}
}
],{multi :true})
Above query uses Aggregation-pipeline in .update() which was introduced in version 4.2, You can also use .updateMany() instead of .update().
It works on documents of below type :
/* 1 */
{
"_id" : ObjectId("5e810f5ec16b5679b43a2f0e"),
"reviews" : [
{
"snid" : '1242'
},
{
"snid" : '8392'
}
]
}
/* 2 */
{
"_id" : ObjectId("5e810f6ac16b5679b43a310c"),
"reviews" : [
{
"snid" : '1242232'
},
{
"snid" : '8391232'
}
]
}
/* 3 */
{
"_id" : ObjectId("5e8110b1c16b5679b43a5148"),
"abc" : 1
}
/* 4 */
{
"_id" : ObjectId("5e8110c3c16b5679b43a52f9"),
"reviews" : []
}
/* 5 */
{
"_id" : ObjectId("5e811359c16b5679b43a9229"),
"reviews" : [
{
"abc" : "1"
}
]
}
But above update query will partially work if you've a doc like below :
{
"_id" : ObjectId("5e811359c16b5679b43a9230"),
"reviews" : [
{
"abc" : "1"
},
{
"snid" : "123"
}
]
}
In that case you need to use $cond to do a conditional check in $map to see if current object has key snid then convert value or else pass on the same object as is to 'reviews' array.
2) Just in Case if your MongoDB version is < 4.2/4.0 & > 3.2 - You can use .bulkWrite() :
Cause you can not use Aggregation Pipeline in update & also $toInt. So you need to do .find() to get entire docs & write code to convert these from strings to integers & use .bulkWrite() to update docs in one update DB call (You can take _id as key for each document).
3) You can also write an aggregation query on existing collection & use $out to update entire collection or write aggregation result to new collection by running just one query. I would prefer to temporarily write it to new collection to check data is correct & rename new collection to what ever is existing by naming existing with something ends with _backup used as backup.
This question already has answers here:
compare two fields of same document [duplicate]
(1 answer)
MongoDb query condition on comparing 2 fields
(4 answers)
Closed 3 years ago.
This is the result of an aggregate query,
{
"_id" : ObjectId("5dab3240dfbe9a15cd69771d"),
"isManual" : false,
"frequency" : 60,
"lastExecuted" : ISODate("2019-10-21T03:38:15.114Z"),
"lastExecutedTimeFromNow" : 129.58105
}
{
"_id" : ObjectId("5dad47c65310a16581cc6294"),
"isManual" : false,
"frequency" : 50,
"lastExecuted" : ISODate("2019-10-25T00:00:00.000Z"),
"lastExecutedTimeFromNow" : 100
}
{
"_id" : ObjectId("5dad48a55310a16581cc6332"),
"isManual" : true,
"frequency" : 100,
"lastExecuted" : ISODate("2019-10-23T00:00:00.000Z"),
"lastExecutedTimeFromNow" : 50
}
I wanted to filter the documents where the field lastExecutedTimeFromNow greater than frequency. But it returns 0 results.
Here's the aggregate query I'm using,
db.getCollection('test').aggregate([
{
$match: {
"lastExecutedTimeFromNow": { $gte: "$frequency" }
}
}
])
Any clue on where I'm going wrong or any help on this would really be great.
You can use $expr but keep in mind it's slower than normal $match
db.getCollection('test').aggregate([
{
$match: {
$expr: {
$gte: [
"$lastExecutedTimeFromNow",
"$frequency"
]
}
}
}
])
Is there a possibility to calculate mathematical operation on already aggregated computed fields?
I have something like this:
([
{
"$unwind" : {
"path" : "$users"
}
},
{
"$match" : {
"users.r" : {
"$exists" : true
}
}
},
{
"$group" : {
"_id" : "$users.r",
"count" : {
"$sum" : 1
}
}
},
])
Which gives an output as:
{ "_id" : "A", "count" : 7 }
{ "_id" : "B", "count" : 49 }
Now I want to divide 7 by 49 or vice versa.
Is there a possibility to do that? I tried $project and $divide but had no luck.
Any help would be really appreciated.
Thank you,
From your question, it looks like you are assuming result count to be 2 only. In that case I can assume users.r can have only 2 values(apart from null).
The simplest thing I suggest is to do this arithmetic via javascript(if you're using it in mongo console) or in case of using it in progam, use the language you're using to access mongo) e.g.
var results = db.collection.aggregate([theAggregatePipelineQuery]).toArray();
print(results[0].count/results[1].count);
EDIT: I am sharing an alternative to above approach because OP commented about the constraint of not using javascript code and the need to be done only via query. Here it is
([
{ /**your existing aggregation stages that results in two rows as described in the question with a count field **/ },
{ $group: {"_id": 1, firstCount: {$first: "$count"}, lastCount: {$last: "$count"}
},
{ $project: { finalResult: { $divide: ['$firstCount','$lastCount']} } }
])
//The returned document has your answer under `finalResult` field
This question already has answers here:
Mongodb sort inner array
(3 answers)
MongoDB group by array inner-elements
(1 answer)
Closed 3 years ago.
I would like to do aggregation by using $project to query array of objects to get single field data and store as array in descending order or reverse array. I had searched for some solutions at stackoverflow but did not see questions with project query then reverse array.
For example below is my mock data:
"models" : [
{
"model" : "abc002",
"total_modules" : 2
},
{
"model" : "abc003",
"total_modules" : 2
},
{
"model" : "abc004",
"total_modules" : 2
},
]
I have tried with the below solution but it is not exactly what I want as the output is slightly different as shown below:
db.collection.aggregate([
{$project: {"models.model":1}}
])
Output:
"models" : [
{
"model" : "abc002"
},
{
"model" : "abc003"
},
{
"model" : "abc004"
}
]
**In fact, I would like to get this output:**
{
models: [ abc004, abc003, abc002 ]
}
OR
{
models: [ {model:abc004}, {model:abc003}, {model:abc002} ]
}
This question already has answers here:
Mongo field A greater than field B
(4 answers)
Closed 5 years ago.
I want to get records of a MongoDB collection that sticks to this condition: fieldA > fieldB + someNaturalValue. This is what I tried so far:
db.getCollection('collection').find({
$where: function() {
return this.fieldA > this.fieldB + 10000}
});
// or
db.getCollection('collection').aggregate([
{ "$project" : {
"sum" : {"$add" : ["$fieldB", 10000]}
}
},
{ "$match" : {
"sum" : {"$lte" : "$fieldA"}
}
}
]);
The issue I face here is the extra value that I need to add in the condition to one of the fields. Those are not working, that value is not taken into account. What I am missing? I appreciate any kind of help.
Sample Data
db.collection.insert({fieldA : 21000, fieldB : 10000}); //1 ok
db.collection.insert({fieldA : 15000, fieldB : 8000}); //2 nok
db.collection.insert({fieldA : 24000, fieldB : 22000}); //3 nok
db.collection.insert({fieldA : 22000, fieldB : 1000}); //4 ok
Adjusted code from the duplicated question:
db.collection.aggregate([
{$project: {
cmp_value: {$cmp: ['$fieldA', {$add: ['$fieldB', 10000]}]},
obj: '$$ROOT'
}},
{$match: {cmp_value: {$gt: 0}}},
{ $replaceRoot: { newRoot: '$obj' } }
])
$where should be avoided where possible. Documentation is quite clear about it:
The $where provides greater flexibility, but requires that the database processes the JavaScript expression or function for each document in the collection