mongodb, how to find in array other array - arrays

I need some help. I am trying to find an array in another array.
Records:
{
"_id" : ObjectId("56b7e6966213e8d142ef55b7"),
"users" : [
ObjectId("56b0e547a838b5a45b4d0100"), // 0
ObjectId("56a7e16e37e5adf32b97cc3d") // 1
]
},
{
"_id" : ObjectId("56b7e6966213e8d142ef55b7"),
"users" : [
ObjectId("56b0e547a838b5a45b4d0100"),
ObjectId("56a7e16e37e5adf32b97cc3d"),
ObjectId("56b7e6b96213e8d142ef55b8")
]
}
I'm trying to find only first record "_id" : ObjectId("56b7e6966213e8d142ef55b7"),
I'm using query:
db.collection.find({"users" : { $in: [
ObjectId("56b0e547a838b5a45b4d0100"),
ObjectId("56a7e16e37e5adf32b97cc3d")
]}})
or
db.collection.find({ $and: [{
"users": ObjectId("56b0e547a838b5a45b4d0100")
}, {
"users": ObjectId("56a7e16e37e5adf32b97cc3d")
}]})
Response gives me two records.
This request gives me the correct response:
db.collection.find({"users" : [
ObjectId("56b0e547a838b5a45b4d0100"), // 0
ObjectId("56a7e16e37e5adf32b97cc3d") // 1
]})
but if record has an array like this:
{
"_id" : ObjectId("56b7e6966213e8d142ef55b7"),
"users" : [
ObjectId("56a7e16e37e5adf32b97cc3d"), // 1
ObjectId("56b0e547a838b5a45b4d0100") // 0
]
}
it doesn't work
$all, $eq, $in also doesn't work
db.rooms.find({
users: {
$all: [
ObjectId("56a7e16e37e5adf32b97cc3d"),
ObjectId("56b0e547a838b5a45b4d0100")
]
}
})
I need to find rows where [1,2] == [1,2] or [1,2] == [2,1]
not where [1,2,3] == [1,2]
I will appreciate if somebody can help me.

If all you expect is the resulting document with "only" those two array entries, then the combination of conditions you want is $all and $size:
db.rooms.find({
"users": {
"$all": [
ObjectId("56b0e547a838b5a45b4d0100"),
ObjectId("56a7e16e37e5adf32b97cc3d")
],
"$size": 2
}
})
Where $all is basically an $and with shorter syntax, and the $size retricts that since the list as n entries, then you only want to match something of that particular length/size.
This is better than specifying the array directly, since it will match in either order, as opposed to this statement that would not match considering the order is different and therefore not an "exact" match:
db.rooms.find({
"users": [
ObjectId("56a7e16e37e5adf32b97cc3d"),
ObjectId("56b0e547a838b5a45b4d0100")
]
})
So logically the array contains "all" the specified entries, and is the same "size" as the input and no different.

Related

PyMongo - How to compare the given array exactly matches with the document

I have a MongoDB document with the following attributes:
{
"label": [
"ibc",
"ibd",
"ibe"
],
"location": "vochelle st"
}
and I have to return the document only if the documents label exactly matches the given array i.e., ["ibc","ibd"] and for the same, I am using the query:
db.collection.find({"location":"vochelle st","dock_label":{"$all":["ibc", "ibd"]}})
Actual Response:
{
"label": [
"ibc",
"ibd",
"ibe"
],
"location": "vochelle st"
}
Expected Response:
{}
Since the label "ibe" doesn't exist in the given array, the expected result has to be the empty dictionary.
Give $size in your query
db.collection.find({
location: "vochelle st",
label: {
$all: [
"ibc",
"ibd"
],
$size: 2
}
})
mongoplayground
Use $setIntersection to intersect both label and input array.
Compare both intersected array (from 1) and label arrays are matched via $eq.
db.collection.find({
"location": "vochelle st",
$expr: {
$eq: [
{
$setIntersection: [
"$label",
[
"ibc",
"ibd"
]
]
},
"$label"
]
}
})
Sample Mongo Playground
If you want to check if the array exactly matches your input, you don't need any operator, just compare it with your value:
db.collection.find({"location":"vochelle st","label": ["ibc", "ibd"]})

Mongodb find comparing diffrent elements from the same array

I have an array with say two elements, want to compare element[0]and element[1], return true if element[0].code== element[1].code
"SourceArray" : [
{
"source" : "Acetone",
"code" : "90915"
},
{
"source" : "Ketone",
"code" : "90915"
}
Use $expr with aggregation expressions, like:
db.collection.find({
$expr: {
$eq: [
"$SourceArray.0.code",
"$SourceArray.1.code"
]
}
})

Trying to filter values of an array within an array in MongoDB

I am new to MongoDB and I'm trying to filter values of an array within an array. An example of the schema is below. The schema is basically a dump of a 3 tiered Dictionary with a simple object of scalars as the leaf node.
The "I" member contains an array of documents (outer array) of key-value pairs with a string key (k), and the value (v) is an array of documents (middle array) of key-value pairs with a date as the key and value is another dictionary, which isn't part of this question.
Basically, what I need to do is retrieve the most recent data from the middle array (Date, key-value) for a given value of the outer array (string, key-value).
(Collection Sample)
{
"_id" : ObjectId("5eacfbe62758834aefdec003"),
"UserId" : UUID("46942978-29f4-4521-9932-840cead6743e"),
"Data" : {
"I" : [
{
"k" : "LRI39",
"v" : [
{
"k" : ISODate("2020-03-11T20:24:41.591Z"),
"v" : [
{
"k" : ISODate("2020-03-11T20:24:41.594Z"),
"v" : {
"Source" : 1,
"Value" : 19
}
}
]
},
{
"k" : ISODate("2020-01-22T11:37:23.393Z"),
"v" : [
{
"k" : ISODate("2020-01-22T11:37:23.412Z"),
"v" : {
"Source" : 1,
"Value" : 20
}
}
]
}
]
},
...
]
}
}
I have been able to generate a document which is basically what you see from "Data" to the end of the sample, being the entire record for LRI39, using:
db.threetier.aggregate([
{
$project: {
"Data.I": {
$filter: {
input: "$Data.I",
as: "item",
cond: {
$eq: [ "$$item.k", "LRI39" ]
}
}
}
}
}
])
However, no matter what I do, I cannot seem to return any subset of the records of the middle array: I get the 2020-03-11 and 2020-01-22 elements or I get nothing.
I have tried adding stages like the below to the projection above, figuring that I would get 1 record (the 2020-01-22 record) but I get both. If I change the date to be in 2019, I get nothing (as expected).
$project: {
"Data.I.v": {
$filter: {
input: "$Data.I.v",
as: "stamp",
cond: { $lt: [ "$$stamp.k", ISODate("2020-02-14T00:00:00Z") ] }
}
}
}
I have also tried:
{ $match: { $expr: { $lt: [ "Data.I.v.k", ISODate("2020-02-14T00:00:00Z") ] } } }
but that returns no results at all (probably because $match works on documents not arrays) as well as trying to unwind the array using $unwind: "$Data.I.v" before the match, but that returns nothing as well.
It seems like I am missing something fundamental here. I do realize that Mongo is designed (I think) to have those array items as documents, but I do want to see if this will work as is.
You will need to unwind both Data.I and Data.I.v, so that you can consider each of the sub-elements separately.
Then reverse sort by the date field.
Group by the _id and key, selecting only the first document in each group.
Finally, replaceRoot so the return is just the selected document.
db.collection.aggregate([
{$unwind: "$Data.I"},
{$unwind: "$Data.I.v"},
{$sort: {"Data.I.v.k": -1}},
{$group: {
_id: {
_id: "$_id",
key: "$Data.I.k"
},
document: {$first: "$$ROOT"}
}},
{$replaceRoot: {newRoot: "$document"}}
])
Playground

Get documents from an array in MongoDB [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 5 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
I have array in subdocument like this
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 1
},
{
"a" : 2
},
{
"a" : 3
},
{
"a" : 4
},
{
"a" : 5
}
]
}
Can I filter subdocument for a > 3
My expect result below
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 4
},
{
"a" : 5
}
]
}
I try to use $elemMatch but returns the first matching element in the array
My query:
db.test.find( { _id" : ObjectId("512e28984815cbfcb21646a7") }, {
list: {
$elemMatch:
{ a: { $gt:3 }
}
}
} )
The result return one element in array
{ "_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ { "a" : 4 } ] }
and I try to use aggregate with $match but not work
db.test.aggregate({$match:{_id:ObjectId("512e28984815cbfcb21646a7"), 'list.a':{$gte:5} }})
It's return all element in array
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 1
},
{
"a" : 2
},
{
"a" : 3
},
{
"a" : 4
},
{
"a" : 5
}
]
}
Can I filter element in array to get result as expect result?
Using aggregate is the right approach, but you need to $unwind the list array before applying the $match so that you can filter individual elements and then use $group to put it back together:
db.test.aggregate([
{ $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
{ $unwind: '$list'},
{ $match: {'list.a': {$gt: 3}}},
{ $group: {_id: '$_id', list: {$push: '$list.a'}}}
])
outputs:
{
"result": [
{
"_id": ObjectId("512e28984815cbfcb21646a7"),
"list": [
4,
5
]
}
],
"ok": 1
}
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter aggregation operator to do this more efficiently by only including the list elements you want during a $project:
db.test.aggregate([
{ $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
{ $project: {
list: {$filter: {
input: '$list',
as: 'item',
cond: {$gt: ['$$item.a', 3]}
}}
}}
])
$and:
get data between 0-5:
cond: {
$and: [
{ $gt: [ "$$item.a", 0 ] },
{ $lt: [ "$$item.a", 5) ] }
]}
Above solution works best if multiple matching sub documents are required.
$elemMatch also comes in very use if single matching sub document is required as output
db.test.find({list: {$elemMatch: {a: 1}}}, {'list.$': 1})
Result:
{
"_id": ObjectId("..."),
"list": [{a: 1}]
}
Use $filter aggregation
Selects a subset of the array to return based on the specified
condition. Returns an array with only those elements that match the
condition. The returned elements are in the original order.
db.test.aggregate([
{$match: {"list.a": {$gt:3}}}, // <-- match only the document which have a matching element
{$project: {
list: {$filter: {
input: "$list",
as: "list",
cond: {$gt: ["$$list.a", 3]} //<-- filter sub-array based on condition
}}
}}
]);

Query collection containing BsonArray

Sorry, first time trying mongo.
Given the following data ...
db.masterList.findOne()
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"MasterDefinition" : {
"Location" : [
"Whole House",
"Master Bedroom",
"Hallway 2"
],
"DeviceType" : [
"Receptacle",
"GFI",
"LED dimmer"
],
"Style" : [
"Decora",
"Standard"
],
"Color" : [
"White",
"Light Almond"
]
}
}
How do I retrieve the contents of the Color array? I expect something like
["White","Light Almond"]
How do I list the 4 arrays directly subordinate to MasterDefintion? I expect to see
["Location","DeviceType","Style","Color"]
Thanks
For the first part, you can simply do a
collection.aggregate({
$project: {
"_id": 0, // exclude the "_id" field from the result
"result": "$MasterDefinition.Color"
}
})
The second part requires a little magic (documentation can be found here: aggregation framework, $project, $objectToArray):
collection.aggregate({
$project: {
"temp": {
$objectToArray: "$MasterDefinition" // transform the "MasterDefinition" subdocument into an array
}
}
}, {
$project:{
"_id": 0, // do not include the "_id" field in the result - this is an optional step
"result": "$temp.k" // only get the keys (as in "k" fiels) from the array
}
})
How do I retrieve the contents of the Color array? I expect something like ["White","Light Almond"]
// the first argument here is a filter, the second argument is a projection
// since you specified no filter I have only included a projection
// this projection tells MongoDB to return the Color subdocument
// from within the MasterDefinition sub document
db.getCollection('masterList').find({}, {'MasterDefinition.Color': 1})
The above command will return:
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"MasterDefinition" : {
"Color" : [
"White",
"Light Almond"
]
}
}
How do I list the 4 arrays directly subordinate to MasterDefintion? I expect to see ["Location","DeviceType","Style","Color"]
This is a bit trickier because "Location","DeviceType","Style","Color" are not elements in an array instead they are the names of attributes in the MasterDefinition subdocument. You can use the $objectToArray aggregation operator to turn these attribute names into an array but the resulting document doesn't looks exactly like what you hoped for. Here's an example ...
db.getCollection('masterList').aggregate([
// creates an array named "categories" from the attributes of the MasterDefinition sub document
{ $project: { categories: { $objectToArray: "$MasterDefinition" } } },
// projects on the keys of the "categories" array
{$project: {'categories.k': 1}}
])
... which produces this output:
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"categories" : [
{
"k" : "Location"
},
{
"k" : "DeviceType"
},
{
"k" : "Style"
},
{
"k" : "Color"
}
]
}

Resources