How can I express `find` conditions in MongoDB accessing document's values? - mongodb

For example, let's put the case I want all documents inside a collection which are inside a range, whose maximum value is inside the document itself:
myDocument {
qty: Number,
maxValue: Number,
}
db.collection.find( { field: { $gt: 0, $lt: maxValue } } );
Is it possible to do something like that in MongoDB?

Yes you can, using $cmp and aggregation https://docs.mongodb.com/v3.4/reference/operator/aggregation/cmp/
db.col.aggregate([
{$project: {compareResult: {$cmp: ['$field','$maxValue']}}},
{$match: {compareResult: -1, field: {$gt: 0}}}
])

First, run this command to get max value of "maxValue" field
db.collection.find({},{maxValue:1}).sort({"maxValue":-1}).limit(1)
Let's assume max value is 3000.
Then run this
db.collection.find({"maxValue":{"$gte":0 ,"$lte":3000}})

Related

How to $inc a field by another field in the same document?

How do I use the value of another field in $inc to define how much to increase?
Example-
Some doc
{_id: obi, length: 256, delta: 6}
Here, I want to increase the length by delta increment.
Pseudocode would be
db.collection.update({}, $inc: {length: $delta});
Thanks for helping...
Simple update operation can't allow to use internal fields in another fields, either its any of the operators,
For the solution you can use update with aggregation pipeline starting from MongoDB 4.2,
use $add/$sum any operator from both to sum the both fields number
db.collection.update({},
[{
$set: {
length: {
$add: ["$length", "$delta"]
}
}
}]
)
Playground

How can i search by _id's feature in mongodb

For example, I want to update half of data whose _id is an odd number. such as:
db.col.updateMany({"$where": "this._id is an odd number"})
Instead of integer, _id is mongo's ObejectId which be regard as hexadecimal "string". It is not supported to code as:
db.col.updateMany(
{"$where": "this._id % 2 = 1"},
{"$set": {"a": 1}}
)
so, what is the correct format?
And what if molding according to _id?
This operation can also be done using two database calls.
Get List of _id from collection.
Push only ODD _id into an array.
Update the collection.
Updating the collection:
db.collection.update(
{ _id: { $in: ['id1', 'id2', 'id3'] } }, // Array with odd _id
{ $set: { urkey : urValue } }
)

MongoDB, right projection subfield [duplicate]

Is it possible to rename the name of fields returned in a find query? I would like to use something like $rename, however I wouldn't like to change the documents I'm accessing. I want just to retrieve them differently, something that works like SELECT COORINATES AS COORDS in SQL.
What I do now:
db.tweets.findOne({}, {'level1.level2.coordinates': 1, _id:0})
{'level1': {'level2': {'coordinates': [10, 20]}}}
What I would like to be returned is:
{'coords': [10, 20]}
So basically using .aggregate() instead of .find():
db.tweets.aggregate([
{ "$project": {
"_id": 0,
"coords": "$level1.level2.coordinates"
}}
])
And that gives you the result that you want.
MongoDB 2.6 and above versions return a "cursor" just like find does.
See $project and other aggregation framework operators for more details.
For most cases you should simply rename the fields as returned from .find() when processing the cursor. For JavaScript as an example, you can use .map() to do this.
From the shell:
db.tweets.find({},{'level1.level2.coordinates': 1, _id:0}).map( doc => {
doc.coords = doc['level1']['level2'].coordinates;
delete doc['level1'];
return doc;
})
Or more inline:
db.tweets.find({},{'level1.level2.coordinates': 1, _id:0}).map( doc =>
({ coords: doc['level1']['level2'].coordinates })
)
This avoids any additional overhead on the server and should be used in such cases where the additional processing overhead would outweigh the gain of actual reduction in size of the data retrieved. In this case ( and most ) it would be minimal and therefore better to re-process the cursor result to restructure.
As mentioned by #Neil Lunn this can be achieved with an aggregation pipeline:
And starting Mongo 4.2, the $replaceWith aggregation operator can be used to replace a document by a sub-document:
// { level1: { level2: { coordinates: [10, 20] }, b: 4 }, a: 3 }
db.collection.aggregate(
{ $replaceWith: { coords: "$level1.level2.coordinates" } }
)
// { "coords" : [ 10, 20 ] }
Since you mention findOne, you can also limit the number of resulting documents to 1 as such:
db.collection.aggregate([
{ $replaceWith: { coords: "$level1.level2.coordinates" } },
{ $limit: 1 }
])
Prior to Mongo 4.2 and starting Mongo 3.4, $replaceRoot can be used in place of $replaceWith:
db.collection.aggregate(
{ $replaceRoot: { newRoot: { coords: "$level1.level2.coordinates" } } }
)
As we know, in general, $project stage takes the field names and specifies 1 or 0/true or false to include the fields in the output or not, we also can specify the value against a field instead of true or false to rename the field. Below is the syntax
db.test_collection.aggregate([
{$group: {
_id: '$field_to_group',
totalCount: {$sum: 1}
}},
{$project: {
_id: false,
renamed_field: '$_id', // here assigning a value instead of 0 or 1 / true or false effectively renames the field.
totalCount: true
}}
])
Stages (>= 4.2)
$addFields : {"New": "$Old"}
$unset : {"$Old": 1}

How to add order in $all operator in MongoDB

How can I get documents from mongo with an array containing some elements but IN THE SAME ORDER?
I know that $all do the job but ignoring the order of elements. The order in my case is important and I can't sort my arrays since it's describing a path that I want to keep the order.
111,222,333 is not the same as 222,111,333
Is there a way to do it using $all or maybe another operator in mongo aggregation framework?
You can avoid the first "intersect" field, is just to give you back as debug what MongoDB make with this command. You should create the $and operator dynamically.
db.Test6.aggregate([
{
$project: {
_id:1,
pages:1,
intersect: {$setIntersection: [[111,666], "$pages"]},
theCondition: {$let: {
vars: {
intersect: {$setIntersection: [[111,666], "$pages"]}
},
in: {
$cond:[ {$and:[
{$eq:[{$arrayElemAt:["$$intersect", 0]}, 111]},
{$eq:[{$arrayElemAt:["$$intersect", 1]}, 666]}
]} , true, false]
}
}
}
}
}
]);

mongoDB - average on array values

I'm trying to compute an average aggregation operation on each values of an array for each documents in my collection.
Document structure
{
myVar: myValue,
[...]
myCoordinates: [
myLng,
myLat
]
}
So, I tried to compute average of myLng and myLat values of myCoordinates array for the whole collection of documents by querying the collection like this :
myColl.aggregate([{
$group: {
_id: 0,
lngAvg: { $avg: "$myCoordinates.0" },
latAvg: { $avg: "$myCoordinates.1" }
}
}])
But unfortunately, it doesn't work and returns me a value of 0 for both lngAvg and latAvg fields.
Have you some ideas? Is this feasible at least?
Positional notation in aggregation seems to still be unsupported, check out this ticket.
As #Sammaye says you'd need to either unwind the array first, or replace your coordinates array with an embedded lng/lat embedded doc, which would make this trivial.
Given the array structure, you might unwind and project the lat/lng like this:
myColl.aggregate([
// unwind the coordinates into separate docs
{$unwind: "$myCoordinates"},
// group back into single docs, projecting the first and last
// coordinates as lng and lat, respectively
{$group: {
_id: "$_id",
lng: {$first: "$myCoordinates"},
lat: {$last: "$myCoordinates"}
}},
// then group as normal for the averaging
{$group: {
_id: 0,
lngAvg: {$avg: "$lng"},
latAvg: {$avg: "$lat"}
}}
]);