In the collection "friendship" I would like to find all Bart's friends that are 10. The $elemMatch query only returns the first matching element of the array : I only get Milhouse. How can I get Milhouse and Martin ?
{ name:'Bart',
age:10
friends:[
{ name:'Milhouse',
age:10
},
{ name:'Nelson',
age:11
},
{ name:'Martin',
age:10
}
]
},
{ name:'Lisa',
age:8
friends:[
...
]
}
Try to use aggregation framework for this task (especially $unwind operator):
db.friendship.aggregate([
{ $match: { name: "Bart" } },
{ $unwind: "$friends" },
{ $match: { "friends.age": 10 } }
]);
Related
i have a collection with more then 1000 documents and there are some documents with same value in some fields, i need to get those
the collection is:
[{_id,fields1,fields2,fields3,etc...}]
what query can i use to get all the elements that have the same 3 fields for example:
[
{_id:1,fields1:'a',fields2:1,fields3:'z'},
{_id:2,fields1:'a',fields2:1,fields3:'z'},
{_id:3,fields1:'f',fields2:2,fields3:'g'},
{_id:4,fields1:'f',fields2:2,fields3:'g'},
{_id:5,fields1:'j',fields2:3,fields3:'g'},
]
i need to get
[
{_id:2,fields1:'a',fields2:1,fields3:'z'},
{_id:4,fields1:'f',fields2:2,fields3:'g'},
]
in this way i can easly get a list of "duplicate" that i can delete if needed, it's not really important get id 2 and 4 or 1 and 3
but 5 would never be included as it's not 'duplicated'
EDIT:
sorry but i forgot to mention that there are some document with null value i need to exclude those
This is the perfect use case of window field. You can use $setWindowFields to compute $rank in the grouping/partition you want. Then, get those rank not equal to 1 to get the duplicates.
db.collection.aggregate([
{
$match: {
fields1: {
$ne: null
},
fields2: {
$ne: null
},
fields3: {
$ne: null
}
}
},
{
"$setWindowFields": {
"partitionBy": {
fields1: "$fields1",
fields2: "$fields2",
fields3: "$fields3"
},
"sortBy": {
"_id": 1
},
"output": {
"duplicateRank": {
"$rank": {}
}
}
}
},
{
$match: {
duplicateRank: {
$ne: 1
}
}
},
{
$unset: "duplicateRank"
}
])
Mongo Playground
I think you can try this aggregation query:
First group by the feilds you want to know if there are multiple values.
It creates an array with the _ids that are repeated.
Then get only where there is more than one ($match).
And last project to get the desired output. I've used the first _id found.
db.collection.aggregate([
{
"$group": {
"_id": {
"fields1": "$fields1",
"fields2": "$fields2",
"fields3": "$fields3"
},
"duplicatesIds": {
"$push": "$_id"
}
}
},
{
"$match": {
"$expr": {
"$gt": [
{
"$size": "$duplicatesIds"
},
1
]
}
}
},
{
"$project": {
"_id": {
"$arrayElemAt": [
"$duplicatesIds",
0
]
},
"fields1": "$_id.fields1",
"fields2": "$_id.fields3",
"fields3": "$_id.fields2"
}
}
])
Example here
I have nested object of objects. Each document in collection looks like this:
{
anything: "whatever",
something: {
// find inside of these document
a: { getThis: "wow" },
b: { getThis: "just wow" },
c: { getThis: "another wow" }
}
}
I would like to find in every getThis from each document in something.
For example I would like to find document which has getThis: "wow".
I've tried to use something like wildcard with *:
{"something.*.getThis": "wow" }
I've also tried $elemMatch but it seems it works only with array;
{ something: { $elemMatch: { getThis: "wow" } } }
You can try using $objectToArray,
$addFields to convert something to array in somethingArr
$match condition getThis is wow or not
$project to remove somethingArr
db.collection.aggregate([
{
$addFields: {
somethingArr: { $objectToArray: "$something" }
}
},
{ $match: { "somethingArr.v.getThis": "wow" } },
{ $project: { somethingArr: 0 } }
])
Playground
Second possible way
$filter input something as array, convert using $objectToArray
filter will check condition getThis is equal to wow or not
db.collection.aggregate([
{
$match: {
$expr: {
$ne: [
[],
{
$filter: {
input: { $objectToArray: "$something" },
cond: { $eq: ["$$this.v.getThis", "wow"] }
}
}
]
}
}
}
])
Playground
Mongodb: 4.0.13
I'm having troubles in understand and get working $expr with arrays.
Let' start and create a new collection (dbRepeatElement) with following document:
db.testRepeatElement.insert([
{
"data" : {
"FlsResSemires_2" : {
"Sospensione" : [
{
"DataInizio" : 1548806400000,
"DataFine" : 1549065600000,
"Motivazione" : "1"
}
]
}
},
"derived" : {
"DATAFINEANNORIFERIMENTO" : 1609372800000,
"regione190" : "190",
"REGAOEROG" : "190209820300",
"REGASLEROG" : "190209"
}
}
])
In a bigger aggregation, following part is not working:
db.testRepeatElement.aggregate([
{
$match: {
$expr: {
$gt: ["$data.FlsResSemires_2.Sospensione.DataInizio", "$derived.DATAFINEANNORIFERIMENTO"]
}
}
}
])
Result: return a match ( wrong! just check dates)
Reading mongodb documentation seems to be, using combination with arrays, aggregation and $expr does not return expected result and you have to specify with element of the array you want to check, like:
db.testRepeatElement.aggregate([
{
$match: {
$expr: {
$gt: ["$data.FlsResSemires_2.0.Sospensione.DataInizio", "$derived.DATAFINEANNORIFERIMENTO"]
}
}
}
])
Result: return no match (right!)
Question: my requirement is to check every element in the array, so how to solve this, without using $unwind? Why there is this kind of result ?
The $filter aggregation operator is used to do the match operation on array elements. The following aggregation query will result only the Sospensione array elements which match the $gt condition:
db.testRepeatElement.aggregate( [
{
$addFields: {
"data.FlsResSemires_2.Sospensione": {
$filter: {
input: "$data.FlsResSemires_2.Sospensione",
cond: {
$gt: [ "$$this.DataInizio", "$derived.DATAFINEANNORIFERIMENTO" ]
}
}
}
}
},
{
$match: {
$expr: {
$gt: [ { $size: "$data.FlsResSemires_2.Sospensione" }, 0 ]
}
}
}
] ).pretty()
I have these documents.
db.test.find({"house.floor":1})
db.test.insertMany([{
"name":"homer",
"house": {
"floor": 1,
"room":
{
"bed": "bed_pink",
"chair":"chair_pink"
}
}
},
{
"name":"marge",
"house": {
"floor": 1,
"room":
{
"bed": "bed_blue",
"chair":"chair_red"
}
}
}]
)
db.test.find({"house.room.bed":"bed_blue"})
I want to return only the value of the last level of my search. in this case:
{
"bed": "bed_blue",
"chair":"chair_red"
}
the answer I get, is the whole document, I want to advance to a certain level of the query. how can I do it?
You can use aggregation for it
db.test.aggregate([
{ $match: { "house.room.bed":"bed_blue" } },
{ $replaceRoot: { newRoot: "$house.room" } }
])
You can use aggregate together with $match and $project for this as follows:
db.test.aggregate([
{
$match: {
"house.room.bed": "bed_blue"
}
},
{
$project: {
"_id": 0,
"bed": "$house.room.bed",
"chair": "$house.room.chair"
}
}
])
Further, you can modify the $project part as needed.
Here is a demo: https://mongoplayground.net/p/Fvll0LFIvQy
This is what i want my aggregation pipeline to look, i just don't know how to properly do it
db.Collection.aggregate([
{
$project: {
all_bills: ‘$all_count’,
settled_bills: { $size: ’$settled’ },
overdue_bills: { $size: ‘$overdue’ },
settled_percentage: { $divide: [‘$settled_bills’, ‘$overdue_bills’] }
}
}
])
I want to use the "settled_bills" and "overdue_bills" fields inside the "settled_percentage" field on same projection pipeline. How to?
From what i can see, i think you want $let.
You can create local variable which can be used inside the $let expression.
Try this:
db.Collection.aggregate([
{
$project: {
all_bills: ‘$all_count’,
settled_bills: { $size: ’$settled’ },
overdue_bills: { $size: ‘$overdue’ },
settled_percentage: {
$let : {
vars : {
local_settled_bills : { $size : "$settled"},
local_overdue_bills : { $size : "$overdue"}
},
in : {
$divide : ["$$local_settled_bills","$$local_overdue_bills"]
}
}
}
}
}
])
Here, you create local varialbes in vars expression, which can be used inside(and only inside in expression). I have created local_settles_bills, and local_overdue_bills, and which can be used in in expression with $$ as prefix.
I hope this helps you out.
Read MongoDb $let documentation for detailed information on $let.
Alternatively, you can do this as well :
db.Collection.aggregate([
{
$project: {
all_bills: ‘$all_count’,
settled_bills: { $size: ’$settled’ },
overdue_bills: { $size: ‘$overdue’ },
settled_percentage: {
$divide : [{"$size" : "$settled_bills"},{"$size":"$overdue_bills"}]
}
}
}
])
So i guess there is no way I can use fields on other fields that co-exist on same projection pipeline.
(assume the settled_bills and overdue_bills consist not just the 'size' but with long query operators )
I'll just do this instead, so i will not repeat the code on the $divide.
db.Collection.aggregate([
{
$project: {
all_bills: ‘$all_count’,
settled_bills: { $size: ’$settled’ },
overdue_bills: { $size: ‘$overdue’ },
},
$project: {
settled_percentage: {
$divide : ['$settled_bills','$overdue_bills']
}
}
}
])