In Mongo query in Subdocuments multiple fields - mongodb

I have a collection in Mongo
{
"_id": 1,
"favorites": {
"artist": "Picasso",
"food": "pizza"
},
"finished": [
17,
3
],
"badges": [
"blue",
"black"
],
"points": [
{
"points": 85,
"bo nus": 20
},
{
"points": 85,
"bonus": 10
}
]
}{
"_id": 2,
"favorites": {
"artist": "Miro",
"food": "meringue"
},
"finished": [
11,
25
],
"badges": [
"green"
],
"points": [
{
"points": 85,
"bonus": 20
},
{
"points": 64,
"bonus": 12
}
]
}{
"_id": 3,
"favorites": {
"artist": "Cassatt",
"food": "cake"
},
"finished": [
6
],
"badges": [
"blue",
"red"
],
"points": [
{
"points": 85,
"bonus": 8
},
{
"points": 55,
"bonus": 20
}
]
}{
"_id": 4,
"favorites": {
"artist": "Chagall",
"food": "chocolate"
},
"finished": [
5,
11
],
"badges": [
"red",
"black"
],
"points": [
{
"points": 53,
"bonus": 15
},
{
"points": 51,
"bonus": 15
}
]
}{
"_id": 5,
"favorites": {
"artist": "Noguchi",
"food": "nougat"
},
"finished": [
14,
6
],
"badges": [
"orange"
],
"points": [
{
"points": 71,
"bonus": 20
}
]
}{
"_id": 6,
"favorites": {
"food": "pizza",
"artist": "Picasso"
},
"finished": [
18,
12
],
"badges": [
"black",
"blue"
],
"points": [
{
"points": 78,
"b onus": 8
},
{
"points": 57,
"bonus": 7
}
]
}
I want to retrieve all elements having points = 85 and bonus = 20.
Query will be
db.temp2.find({"points":{"points":85,"bonus":20}})
it returns documents with id : 1 and 2.
Now if i want to retrieve elements having ( points=85 and bonus = 20 ) and another sub-documents with {points=85 and bonus > 10). basically i want to retrieve element with id = 2
if query is
db.temp2.find({$and:[{"points":{"points":85,"bonus":20}},{"points":{"points":64,"bonus":{$gte:10}}}]}).pretty()
it gives no results whereas query
db.temp2.find({$and:[{"points":{"$elemMatch":{"points":85,"bonus":20}}},{"points":{"$elemMatch":{"points":64,"bonus":{$gte:10}}}}]})
gives me id=2.
Same thing i tried with anther sets
[
{
"name": "User1",
"tags": [
{
"k": "group",
"v": "test"
},
{
"k": "color",
"v": "blue"
}
]
},
{
"name": "User2",
"tags": [
{
"k": "group",
"v": "dev"
},
{
"k": "color",
"v": "blue"
}
]
},
{
"name": "User3",
"tags": [
{
"k": "group",
"v": "dev"
},
{
"k": "color",
"v": "red"
}
]
}
]
and if you want to find out elements having
"tags": [
{
"k": "group",
"v": "dev"
},
{
"k": "color",
"v": "blue"
}
]
query :
db.temp4.find({$and:[{"tags":{"k":"group","v":"dev"}},{"tags":{"k":"color","v":"blue"}}]})
and
db.temp4.find({$and:[{"tags":{"$elemMatch":{"k":"group","v":"dev"}}},{"tags":{"$elemMatch":{"k":"color","v":"blue"}}}]})
in both case you will get response.
Please help me to understand when to use $elemMatch and "$and".
Thanks in advance.
Sorry for grammatical mistakes.

The below query should work. As it is an embedded array, the "points.bonus" and "points.points" should be referred like this when $gte is used.
db.collection.find({$and:[{"points":{"points":85,"bonus":20}}, {"points.points" : 64, "points.bonus" : {$gte : 10}}]})
In the second example, there is no $gte. So, you are getting response for both queries.

Related

How can I do an inner join of two collections in mongodb

// orders collection
[
{
"id": 1,
"orderName": "a",
"seqId": 100,
"etc": [],
"desc": [],
},
{
"id": 2,
"orderName": "b",
"seqId": 200,
"etc": [],
"desc": []
},
{
"id": 3,
"orderName": "c",
"seqId": 100,
"etc": [],
"desc": [],
},
]
// goods collection
[
{
"id": 1,
"title": "example1",
"items": [
{
"id": 10,
"details": [
{
"id": 100
},
{
"id": 101,
}
]
},
{
"id": 20,
"details": [
{
"id": 102,
},
{
"id": 103,
}
]
},
]
},
[
{
"id": 2,
"title": "example2",
"items": [
{
"id": 30,
"details": [
{
"id": 200
},
{
"id": 201
}
]
},
{
"id": 40,
"details": [
{
"id": 202
},
{
"id": 203
}
]
},
]
},
]
When the value of the seqId field of the document whose etc field and desc field arrays of the orders collection are empty and the value of the "goods.details.id field of the goods collection are the same, I want to get the following output. How can I do that?
[
{orderName: "a", title: "example1"},
{orderName: "b", title: "example2"},
{orderName: "c", title: "example1"},
]
Additionally, I would like to perform a sum operation based on the title of the goods
collection.
[
{"example1": 2},
{"example2": 1}
]
Simply perform a $lookup between orders.seqId and goods.items.details.id. Use $unwind to eliminate empty lookups(i.e. inner join behaviour). Finally, do a $group with $sum to get the count.
db.orders.aggregate([
{
"$match": {
"etc": [],
"desc": []
}
},
{
"$lookup": {
"from": "goods",
"localField": "seqId",
"foreignField": "items.details.id",
"pipeline": [
{
$project: {
_id: 0,
title: 1
}
}
],
"as": "goodsLookup"
}
},
{
"$unwind": "$goodsLookup"
},
{
$group: {
_id: "$goodsLookup.title",
cnt: {
$sum: 1
}
}
}
])
Mongo Playground

Filter key/value array by value in mongodb

I have this sample data:
[
{
"id": 1,
"marcadores": [
{ "k": "G", "v": "00" },
{ "k": "1", "v": "A" },
]
},
{
"id": 2,
"marcadores": [
{ "k": "1", "v": "A" },
]
},
{
"id": 3,
"marcadores": [
{ "k": "G", "v": "03" },
{ "k": "P", "v": "55" }
]
}
]
I would like to filter those documents with these critera:
marcadores.k: "G" and marcadores.v: { $ne: "00" } ($elemMatch). In the example, "id": 3 meets this criteria
or
document has no marcadores.k: "G". In the example, "id": 2 meets this criteria.
Expected output:
[
{
"id": 2,
"marcadores": [
{ "k": "1", "v": "A" },
]
},
{
"id": 3,
"marcadores": [
{ "k": "G", "v": "03" },
{ "k": "P", "v": "55" }
]
}
]
What's the best/cleanest way to solve this query?
You can use this playground.
Using find we can use $not with $elemMatch:
db.collection.find(
{marcadores: {$not: {$elemMatch: {"k": "G", "v": "00"}}}}
)
See how it works on the playground example - find

Get a list of values by key filter by condition that change for each key

I have a db like that:
[
{
"key": 1,
"value": 1,
"other_value": 7
},
{
"key": 1,
"value": 21,
"other_value": 8
},
{
"key": 1,
"value": 3,
"other_value": 10
},
{
"key": 2,
"value": 12,
"other_value": 0
},
{
"key": 2,
"value": 13,
"other_value": 4
},
{
"key": 2,
"value": 14,
"other_value": 5
},
]
I try to have a list of "value" by "key" where "other_value" is greater or equal to the "max other_value of the key" - 2.
For example, for the key: 1, the max "other_value" is 10. The array for the key: 1 is all "value" where
"other_value" is greater or equal to 8(10-2)
The expected result is:
[
{
"_id": 1,
"array": [
{
"value": 21
},
{
"value": 3
}
]
},
{
"_id": 2,
"array": [
{
"value": 13
},
{
"value": 14
}
]
}
]
I start with this but i'm block with the condition.
db.collection.aggregate({
"$group": {
"_id": "$key",
"array": {
"$push": {
"$cond": [
{ /* condition */ },
{ "value": "$value" },
"$$REMOVE"
]
}
}
}
})
Someone have any idea ?

How to group data using mongo-template

I have to filter data based on a criteria and set a field isObtained true or false. Then I am trying to group the data based on field grade. This is my data:
{
"school": "xyz",
"studentName": "John Doe",
"grade": "first",
"Hobbies": [
"Painting",
"Singing"
],
"language": [
"English"
],
"sport": "Badminton",
"totalStudyHours": 85
},
{
"school": xyz,
"studentName": "Jane Doe",
"grade": "third",
"Hobbies": [
"Painting",
],
"language": [
"Spanish"
],
"sport": "Karate",
"totalStudyHours": 65
},
{
"school": "xyz",
"studentName": "joey",
"grade": "first",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Cricket",
"totalStudyHours": 75,
"ispassed": true,
},
{
"studentName": "jason",
"grade": "third",
"Hobbies": [
"Painting",
],
"language": [
"English",
"Spanish"
],
"sport": "Tennis",
"totalStudyHours": 95,
"isObtained": true
},
{
"studentName": "mike",
"grade": "third",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Badminton",
"totalStudyHours": 70,
"isObtained": true
}
The expected output is
[
{
"grade": "first",
"values": [
{
"studentName": "John Doe",
"grade": "first",
"Hobbies": [
"Painting",
"Singing"
],
"language": [
"English"
],
"sport": "Badminton",
"totalStudyHours": 85,
"isObtained": true
},
{
"studentName": "joey",
"grade": "first",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Cricket",
"totalStudyHours": 75,
"isObtained": true
}
]
},
{
"grade": "third",
"values": [
{
"studentName": "jason",
"grade": "third",
"Hobbies": [
"Painting",
],
"language": [
"English",
"Spanish"
],
"sport": "Tennis",
"totalStudyHours": 95,
"isObtained": true
},
{
"studentName": "mike",
"grade": "third",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Badminton",
"totalStudyHours": 70,
"isObtained": true
}
]
}
]
In the mongoDb query, the isObtained field is set based on what field we want. For example, if we want records with sport as "Badminton" then the isObtained field will be true when sport is Badminton and false otherwise.
Here is the query, but I am facing problem in grouping based on grade.
db.students.aggregate([
{
"$match": {
"$and": [
{
"school": "xyz"
}
]
}
},
{
"$project": {
"sport": 1,
"language": 1,
"hobbies": 1,
"isObtained": {
"$and": [
{
"$eq": [
"$sport",
"Badminton"
]
}
]
}
}
}
$match your conditions
$group by grade and make array of root documents in values,
define required fields and check condition created field isObtained if sport is Badminton then true otherwise false
db.students.aggregate([
{ $match: { school: "xyz" } },
{
$group: {
_id: "$grade",
values: {
$push: {
sport: "$sport",
language: "$language",
Hobbies: "$Hobbies",
isObtained: {
$cond: [{ $eq: ["$sport", "Badminton"] }, true, false]
}
}
}
}
}
])
Playground
If you want to go with dynamic approach then try $mergeObjects with $$ROOT,
Playground

Merging an array while grouping from MongoDB collection

I have a collection that looks something like:
[
{
"_id": "5f0307520ac9361c0d7088e2",
"productId": 1,
"stock": 10,
"unit": "item",
"price": 20,
"images": [
"http://productimages.com/1/001.jpg",
"http://productimages.com/1/002.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e3",
"productId": 1,
"stock": 20,
"unit": "item",
"price": 30,
"images": [
"http://productimages.com/1/003.jpg",
"http://productimages.com/1/004.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e4",
"productId": 2,
"stock": 5,
"unit": "item",
"price": 15,
"images": [
"http://productimages.com/2/001.jpg",
"http://productimages.com/2/002.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e5",
"productId": 2,
"stock": 5,
"unit": "item",
"price": 12,
"images": [
"http://productimages.com/2/003.jpg",
"http://productimages.com/2/004.jpg"
]
}
]
And I aggregate it as follows:
db.variants.aggregate([
{
"$group": {
"_id": "$productId",
"price": { "$min": "$price" },
"stock": { "$sum": "$stock" },
"unit": { "$first": "$unit" },
"images": { "$push": "$images" },
"variants": { "$push": "$$ROOT" }
}
}
]).pretty()
which produces the following output:
[
{
"_id": 2,
"price": 12,
"stock": 10,
"unit": "item",
"images": [
[
"http://productimages.com/2/001.jpg",
"http://productimages.com/2/002.jpg"
],
[
"http://productimages.com/2/003.jpg",
"http://productimages.com/2/004.jpg"
]
],
"variants": [
{
"_id": "5f0307520ac9361c0d7088e4",
"productId": 2,
"stock": 5,
"unit": "item",
"price": 15,
"images": [
"http://productimages.com/2/001.jpg",
"http://productimages.com/2/002.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e5",
"productId": 2,
"stock": 5,
"unit": "item",
"price": 12,
"images": [
"http://productimages.com/2/003.jpg",
"http://productimages.com/2/004.jpg"
]
}
]
},
{
"_id": 1,
"price": 20,
"stock": 30,
"unit": "item",
"images": [
[
"http://productimages.com/1/001.jpg",
"http://productimages.com/1/002.jpg"
],
[
"http://productimages.com/1/003.jpg",
"http://productimages.com/1/004.jpg"
]
],
"variants": [
{
"_id": "5f0307520ac9361c0d7088e2",
"productId": 1,
"stock": 10,
"unit": "item",
"price": 20,
"images": [
"http://productimages.com/1/001.jpg",
"http://productimages.com/1/002.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e3",
"productId": 1,
"stock": 20,
"unit": "item",
"price": 30,
"images": [
"http://productimages.com/1/003.jpg",
"http://productimages.com/1/004.jpg"
]
}
]
}
]
however I would like to get
[
{
"_id": 2,
"price": 12,
"stock": 10,
"unit": "item",
"images": [
"http://productimages.com/2/001.jpg",
"http://productimages.com/2/002.jpg",
"http://productimages.com/2/003.jpg",
"http://productimages.com/2/004.jpg"
],
"variants": [
{
"_id": "5f0307520ac9361c0d7088e4",
"productId": 2,
"stock": 5,
"unit": "item",
"price": 15,
"images": [
"http://productimages.com/2/001.jpg",
"http://productimages.com/2/002.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e5",
"productId": 2,
"stock": 5,
"unit": "item",
"price": 12,
"images": [
"http://productimages.com/2/003.jpg",
"http://productimages.com/2/004.jpg"
]
}
]
},
{
"_id": 1,
"price": 20,
"stock": 30,
"unit": "item",
"images": [
"http://productimages.com/1/001.jpg",
"http://productimages.com/1/002.jpg",
"http://productimages.com/1/003.jpg",
"http://productimages.com/1/004.jpg"
],
"variants": [
{
"_id": "5f0307520ac9361c0d7088e2",
"productId": 1,
"stock": 10,
"unit": "item",
"price": 20,
"images": [
"http://productimages.com/1/001.jpg",
"http://productimages.com/1/002.jpg"
]
},
{
"_id": "5f0307520ac9361c0d7088e3",
"productId": 1,
"stock": 20,
"unit": "item",
"price": 30,
"images": [
"http://productimages.com/1/003.jpg",
"http://productimages.com/1/004.jpg"
]
}
]
}
]
instead. I have tried replacing the images expression with images.* but this produced an empty set.
I have also tried adding a $reduce projection to the pipeline as shown at combine array fields into a single array field mongo:
db.variants.aggregate([
{
"$group": {
"_id": "$productId",
"price": { "$min": "$price" },
"stock": { "$sum": "$stock" },
"unit": { "$first": "$unit" },
"images": { "$push": "$images" },
"variants": { "$push": "$$ROOT" }
}
},
{
"$project": {
"images": {
"$reduce": {
"input": { "$concatArrays": ["images.*"] },
"initialValue": [],
"in": { "$setUnion": ["$$this", "$$value"] }
}
}
}
}
]).pretty()
which fails with:
{
"ok" : 0,
"errmsg" : "Failed to optimize pipeline :: caused by :: $concatArrays only supports arrays, not string",
"code" : 28664,
"codeName" : "Location28664"
}
You can use $concatArrays together with $reduce.
Instead of $project you can use $addFields, available from MongoDB v3.4 or $set available from v4.2 to keep other fields
db.variants.aggregate([
{
"$group": {
"_id": "$productId",
"price": {
"$min": "$price"
},
"stock": {
"$sum": "$stock"
},
"unit": {
"$first": "$unit"
},
"images": {
"$push": "$images"
},
"variants": {
"$push": "$$ROOT"
}
}
},
{
"$addFields": { // or $set
"images": {
"$reduce": {
"input": "$images",
"initialValue": [],
"in": {
"$concatArrays": ["$$value", "$$this"]
}
}
}
}
}
])