MongoDB - most efficient way to query and project matching nested array object - mongodb

My doc structure in "test" collection looks like below:
{
"_id": "a1",
"t": [
{
"tId": "t1",
"do-not-project": "blah",
"b": [
{
"bId": "b1",
"name": "abc",
"do-not-project": "blah"
},
{
"bId": "b2",
"name": "def",
"do-not-project": "blah"
}
]
},
{
"tId": "t2",
"do-not-project": "blah",
"b": [
{
"bId": "b3",
"name": "ghi",
"do-not-project": "blah"
},
{
"bId": "b4",
"name": "jkl",
"do-not-project": "blah"
}
]
}
]
}
What's the most efficient way to query and project the below output if I know values for _id ("a1"), tId ("t2") and bId ("b3")? Aka only the matching nested array object and projected fields? Is this possible without aggregation at all?
{
"_id": "a1",
"t": [
{
"tId": "t2",
"b": [
{
"bId": "b3",
"name": "ghi"
}
]
}
]
}

According to MongoDB documentation, the find method has the following signature:
db.collection.find($query, $project)
So what you are looking for is probably something like:
db.collection.find(
{_id: "a1", "t"."tId": "t2", "t"."b"."bId": "b3" },
{"t"."tId": 1, "t"."b"."bId": 1, "t"."b"."name" :1}
)

Related

Strange MongoDB $setIntersection behaviour

I want to query a match between records in my db based on certain tags. The match would be calculated based on a formula and the intersection of the tags. Now, even querying the intersection doesn't work...always. Sometimes it does, sometimes it doesn't. In my example, if I change the displayName attribute to something else (add or remove one character, the query works. In its current state (for demo purposes) it doesn't as it does not deliver the one intersection match for the last doc with id 3.
https://mongoplayground.net/p/KAYPoV29RFO
That's my query:
db.collection.aggregate([
{
$match: {
"_id": "1"
}
},
{
"$lookup": {
from: "collection",
let: {
"criteria": "$tags"
},
pipeline: [
{
$project: {
"match": {
$setIntersection: [
"$tags",
"$$criteria"
]
},
}
}
],
as: "result"
}
},
{
$project: {
"tags": 0
}
},
])
Here is the example data (simplified):
[
{ "_id": "1", "tags": [{ "_id": "a", "displayName": "a", "level": 1}, {"_id": "b", "displayName": "b", "level": 2}, {"_id": "c", "displayName": "c", "level": 3}]},
{"_id": "2", "tags": [{"_id": "a", "displayName": "a", "level": 1}, {"_id": "b", "displayName": "b", "level": 2}]},
{"_id": "3", "tags": [{"_id": "a", "displayName": "a", "level": 1}, {"_id": "d", "displayName": "d", "level": 4}]}
]
and the result as it is: (expected is three matches for id 1, 2 matches for id 2 and one for the last id. However, the last result has 0 elements in the intersection result. Again, when i change "displayName" to "displayNam" or "displayNames" (obviously in all docs), it give the correct result...
[{
"_id": "1", "result": [
{"_id": "1", "match": [{"_id": "a", "displayName": "a", "level": 1}, {"_id": "b", "displayName": "b", "level": 2},{"_id": "c","displayName": "c","level": 3}]},
{"_id": "2", "match": [{"_id": "a", "displayName": "a", "level": 1}, {"_id": "b", "displayName": "b", "level": 2}]},
{"_id": "3","match": [*here should be the match to _id: "a", but it's not (always) there*]}
]
}]
Does anyone have an idea what I am missing here?

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

MongoDB distinct returns empty

I can't get collection.distinct to work for me on db.version '4.4.3'. I'm following the example at https://docs.mongodb.com/manual/reference/command/distinct/
{ "_id": 1, "dept": "A", "item": { "sku": "111", "color": "red" }, "sizes": [ "S", "M" ] }
{ "_id": 2, "dept": "A", "item": { "sku": "111", "color": "blue" }, "sizes": [ "M", "L" ] }
{ "_id": 3, "dept": "B", "item": { "sku": "222", "color": "blue" }, "sizes": "S" }
{ "_id": 4, "dept": "A", "item": { "sku": "333", "color": "black" }, "sizes": [ "S" ] }
Then run
db.runCommand ( { distinct: "inventory", key: "dept" } )
I get
{ values: [], ok: 1 }
This doesn't work either:
db.inventory.distinct( "dept" )
[]
I can't get it to work on a local server or a remote server. What gives?

MongoDB - Average fields values over documents

I have a collection with this schema:
{
"fields":
{
"field1": [
{"name": "abc", "value": 2},
{"name": "bcd", "value": 4},
{"name": "cde", "value": 6}
],
"field2": [
{"name": "dec", "value": 3},
{"name": "das", "value": 8},
{"name": "pam", "value": 10}
]
}
},
{
"fields":
{
"field1": [
{"name": "abc", "value": 7},
{"name": "cde", "value": 12}
],
"field2": [
{"name": "dec", "value": 3},
{"name": "das", "value": 8},
{"name": "pam", "value": 10}
]
}
}
What I'm trying to obtain is e.g. the average values of all members of 'field1', evaluating 0 if a member exist in a document but not in another (like 'bcd').
So in this example I should get:
{
'_id': 'abc',
'avg': 4.5
},
{
'_id': 'bcd',
'avg': 2
},
{
'_id': 'cde',
'avg': 9
}
I wrote this aggregation query but I'm pretty sure there is something wrong with it:
db.statuses.aggregate([
{
$unwind: '$fields.field1'
},
{
$group: {
_id: '$fields.field1.name',
avg: {
$avg: '$fields.field1.value'
}
}
},
{
$sort: {
avg: -1
}
}
])
I think I should add a step before the average calculation in which I have to build an array of all values for each name (0 if the name does not exist in a document), and then evaluate the average on these arrays. Am I right?
How could I do this?

Why is Robomongo returning an object from db.collection.distinct() instead of an array?

Using the example from the MongoDB reference, I'd expect db.inventory.distinct("dept"); to return an array ["A", "B"], and that's exactly what happens when I run this from the shell. Using Robomongo (on OS X) I instead get an object with name-value pairs, like so: { "0" :"A", "1": "B" }.
This is the setup:
db.inventory.drop();
db.inventory.insert([
{ "_id": 1, "dept": "A", "item": { "sku": "111", "color": "red" },
{ "_id": 2, "dept": "A", "item": { "sku": "111", "color": "blue" },
{ "_id": 3, "dept": "B", "item": { "sku": "222", "color": "blue" },
{ "_id": 4, "dept": "A", "item": { "sku": "333", "color": "black" }
]);
Why is Robomongo behaving different? Can I do anything about it?