Let's say I have some documents that have an array like this:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"letters": ["a","b","c","d"]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"letters": ["a","b"]
},
{
"_id": ObjectId("5a934e000102030405000002"),
"letters": ["a"]
},
{
"_id": ObjectId("5a934e000102030405000003"),
"letters": ["x","a","b"]
}
]
I want to retrieve all the documents whose letters array start with an n length array. For example: ["a","b"]
So the result would be like this:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"letters": ["a","b","c","d"]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"letters": ["a","b"]
}
]
I have searched on mongo docs and stack overflow, and the only thing that's close is using $all operator but that's not exactly what I want.
I think it could be done by first slicing the array and then matching it with the query array, but I couldn't find anything.
You can simply use array index in match query,
check 0 index for a value
check 1 index for b value
db.collection.find({
"letters.0": "a",
"letters.1": "b"
})
Playground
Query
slice and take the first 2 of $letters
check if equal with ["a" "b"]
*this is like general solution for any array, to make it work you can replace the 2 with the array size, and the ["a" "b"] with your array
Playmongo
aggregate(
[{"$match": {"$expr": {"$eq": [{"$slice": ["$letters", 2]}, ["a", "b"]]}}}])
Related
I have a set of objects in a MongoDB. The object includes an array of types. Now I am connecting to the DB with Mongoose and would like to now the number of objects for each Type.
For example my objects look like
{
"name": "abc",
"tags": ["a","b","c"]
}
Now I would like to get the total number of Objects which for example have the Tag "a"
I am connecting to the MongoDB with a NodeJS Backend using Mongoose
Thanks for your Ideas on how to query this efficiently.
Addition:
In the case where I don't know what kind of different tags are existing in the different objects and I would like to get an overview of all tags, how do I need to query on this one? For better understanding I give an example.
Two objects in the database:
{
"name": "abc",
"tags": ["a","b","c"]
},
{
"name": "abc",
"tags": ["a","b"]
}
the function/query should give a response of something like this:
{
"a": 2,
"b": 2,
"c": 1
}
collection.aggregate([
{$unwind: "$tags" },
{$group: {
_id: "$tags",
count: {$sum : 1}
}},
]);
this will give output like this:
/* 1 */
{
"_id" : "c",
"count" : 2
}
/* 2 */
{
"_id" : "b",
"count" : 3
}
/* 3 */
{
"_id" : "a",
"count" : 2
}
Use count which is a collection method. It returns the count only instead of all documents. If You need the documents , replace count with find
collection.count({tags:"a"})
I have an array A in memory created at runtime and another array B saved in a mongo database. How can I efficiently get all the elements from A that are not in B?
You can assume that the array stored in mongodb is several orders of magnitude bigger than the array created at runtime, for that reason I think that obtaining the full array from mongo and computing the result would not be efficient, but I have not found any query operation in mongo that allows me to compute the result I want.
Note that the $nin operator does the opposite of what I want, i.e., it retrieves the elements from B that are not in A.
Example:
Array A, created in my appliction at runtime, is [2, 3, 4].
Array B, stored in mongodb, is [1, 3, 5, 6, 7, 10].
The result I expect is [2, 4].
The only things that "modify" the document in response are .aggregate() and .mapReduce(), where the former is the better option.
In that case you are asking for $setDifference which compares the "sets" and returns the "difference" between the two.
So representing a document with your array:
db.collection.insert({ "b": [1, 3, 5, 6, 7, 10] })
Run the aggregation:
db.collection.aggregate([{ "$project": { "c": { "$setDifference": [ [2,3,4], "$b" ] } } }])
Which returns:
{ "_id" : ObjectId("596005eace45be96e2cb221b"), "c" : [ 2, 4 ] }
If you do not want "sets" and instead want to supply an array like [2,3,4,4] then you can compare with $filter and $in instead, if you have MongoDB 3.4 at least:
db.collection.aggregate([
{ "$project": {
"c": {
"$filter": {
"input": [2,3,4,4],
"as": "a",
"cond": {
"$not": { "$in": [ "$$a", "$b" ] }
}
}
}
}}
])
Or with $filter and $anyElementTrue in earlier versions:
db.collection.aggregate([
{ "$project": {
"c": {
"$filter": {
"input": [2,3,4,4],
"as": "a",
"cond": {
"$not": {
"$anyElementTrue": {
"$map": {
"input": "$b",
"as": "b",
"in": {
"$eq": [ "$$a", "$$b" ]
}
}
}
}
}
}
}
}}
])
Where both would return:
{ "_id" : ObjectId("596005eace45be96e2cb221b"), "c" : [ 2, 4, 4 ] }
Which is of course "not a set" since the 4 was provided as input "twice" and is therefore returned "twice" as well.
I have a problem which I have already solved with $unwind and $group, but I am really interested in whether it is possible to solve with $reduce.
Currently using MongoDb 3.4
Here is the input:
{
"myArray": [
{"foo":"x",
"bar":"y",
"important": {
"imp_1": "a",
"imp_2": "b"
}
},
{
"foo":"x",
"bar":"y",
"important": {
"imp_1": "aa",
"imp_3": "bb"
}
}
]
}
And here is what I would like to achieve:
{
"myArray": [
{"foo":"x",
"bar":"y",
"important": [
{"k":"imp_1", "v":"a"},
{"k":"imp_2": "v":"b"}
]
}
},
{
"foo":"x",
"bar":"y",
"important": [
{"k":"imp_1","v": "aa"},
{"k":"imp_3","v": "bb"}
]
}
]
}
Is it possible to solve it with 1 $reduce?
My problem is, that it created only 1 "important" array, instead of 2 for each object in the initial array.
Is $reduce at all better then a $unwind, $group and a $project right after each other?
Thx & Regards,
Grunci
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.
I have a collection like the following:-
{
_id: 5,
"org_name": "abc",
"items": [
{
"item_id": "10",
"data": [
// Values goes here
]
},
{
"item_id": "11",
"data": [
// Values goes here
]
}
]
},
// Another sub document
{
_id: 6,
"org_name": "sony",
"items": [
{
"item_id": "10",
"data": [
// Values goes here
]
},
{
"item_id": "11",
"data": [
// Values goes here
]
}
]
}
Each sub document corresponds to individual organizations and each organization has an array of items in them.
What I need is to get the select individual elements from the items array, by providing item_id.
I already tried this:-
db.organizations.find({"_id": 5}, {items: {$elemMatch: {"item_id": {$in: ["10", "11"]}}}})
But it is returning either the item list with *item_id* "10" OR the item list with *item_id* "11".
What I need is is the get values for both item_id 10 and 11 for the organization "abc". Please help.
update2:
db.organizations.aggregate([
// you can remove this to return all your data
{$match:{_id:5}},
// unwind array of items
{$unwind:"$items"},
// filter out all items not in 10, 11
{$match:{"items.item_id":{"$in":["10", "11"]}}},
// aggregate again into array
{$group:{_id:"$_id", "items":{$push:"$items"}}}
])
update:
db.organizations.find({
"_id": 5,
items: {$elemMatch: {"item_id": {$in: ["10", "11"]}}}
})
old Looks like you need aggregation framework, particularly $unwind operator:
db.organizations.aggregate([
{$match:{_id:5}}, // you can remove this to return all your data
{$unwind:"$items"}
])