MongoDB find the intersection of arrays - arrays

docs:
order1.filter = ['tag1','tag2']
order2.filter = ['tag1','tag2','tag3']
want to get:
query ['tag1','tag2'] -> (only order1)
query ['tag1','tag2','tag3'] -> (order1 and order2)
query ['tag1','tag2','tag3','tag4', etc ] -> (order1 and order2)
and
query ['tag1','tag3'] -> (null)
query ['tag2','tag3'] -> (null)
All values ​​order.filter should be necessarily in the query array
How to do it? Tried directives $all, $in :(

You can do this with aggregation framework (there is no way to do this with regular find that I know of).
I think this is basically a duplicate so adjusting that code for your fields you get something like:
//sample documents:
> db.docs.find({},{_id:0})
{ "order" : 1, "filter" : [ "t1", "t2" ] }
{ "order" : 2, "filter" : [ "t1", "t2", "t3" ] }
var tagArray = [ "t1", "t2" ]; // array to "match"
db.docs.aggregate( [
{
"$project" : {
"order" : 1,
"filter" : 1,
"killFlag" : {
"$const" : [
true,
false
]
}
}
},
{
"$unwind" : "$filter"
},
{
"$unwind" : "$killFlag"
},
{
"$match" : {
"$nor" : [
{
"filter" : {
"$in" : tagArray
},
"killFlag" : true
}
]
}
},
{
"$group" : {
"_id" : "$order",
"filter" : {
"$addToSet" : "$filter"
},
"killFlag" : {
"$max" : "$killFlag"
}
}
},
{
"$match" : {
"killFlag" : false
}
},
{
"$project" : {
"_id" : 1,
"filter" : 1
}
}
]);

Related

MongoDB: find in array of objects where items are only one value

The data is like this:
{
"_id" : ObjectId("5ae9f2188857ce20f516315c"),
"meta" : {
"participants" : [
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
},
{
"gender" : [
{
"text" : "männlich",
"id" : "LABEL.MALE"
}
]
},
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
}
]
}
}
{
"_id" : ObjectId("5af00e1070bb5a707634cb12"),
"meta" : {
"participants" : [
{
"gender" : [
{
"text" : "männlich",
"id" : "LABEL.MALE"
}
]
},
{
"gender" : [
{
"text" : "männlich",
"id" : "LABEL.MALE"
}
]
}
]
}
}
{
"_id" : ObjectId("5af1ef01cfd317006694a6e6"),
"meta" : {
"participants" : [
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
},
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
}
]
}
}
So meta.participants contains some items with different properties e.g. gender with is also an array of one item (don't ask, historic reasons; it's always one item, never two and never empty).
I need a query which returns the documents which contains only male participants (the second doc 5af00e1070bb5a707634cb12).
I already tried my luck but I can't get it right.
These queries give me every doc which has a male participant:
{'meta.participants.gender.id': 'LABEL.MALE'}
{'meta.participants.gender': { $elemMatch: {id: 'LABEL.MALE'}}}
These queries give me 0 results..
{'meta.participants.gender': {id: 'LABEL.MALE'}}
{'meta.participants.gender[0]': { $elemMatch: {id: 'LABEL.MALE'}}}
Try the below:
db.collection.find({
"meta.participants": {
"$not": {
"$elemMatch": {
"gender.id": {
"$nin": [
"LABEL.MALE"
]
}
}
}
}
})
The query to retrieve documents with gender.id field do not have LABEL.FEMALE.
db.test.find( { "meta.participants.gender.id": { $ne: "LABEL.FEMALE" } } )
As such you don't need to use the $elemMatch for Single Query Condition (but, it not an error and the results will be same).

Aggregating MongoDB, displaying top results from two separate collections

I have am trying to perform an aggregate function on my collection but I can't seem to fit the right query for the job.
My goal is to display the top 2 fastest laps on all maps and show the associated user first name and last name.
Here is my stats collections:
{
"_id" : ObjectId("5c86674d87e8cd468c850c86"),
"lapTime" : "1:32:29",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:49:01.472Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c8667ec87e8cd468c850c87"),
"lapTime" : "2:32:34",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:51:40.895Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c86674x87e8Sd567c120c86"),
"lapTime" : "1:12:29",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T10:49:01.472Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c8667f887e8cd468c850c88"),
"lapTime" : "1:88:29",
"map" : "Italy",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:51:52.727Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c866970c65910291c6f2000"),
"lapTime" : "1:34:29",
"map" : "Italy",
"driver" : [
ObjectId("5c80f78ca0ecdf26c83dfc8a")
],
"date" : ISODate("2019-03-11T13:58:08.135Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c868532b5c50c17b0917f9e"),
"lapTime" : "1:43:33",
"map" : "Italy",
"driver" : [
ObjectId("5c80f78ca0ecdf26c83dfc8a")
],
"date" : ISODate("2019-03-11T15:56:34.869Z"),
"__v" : 0
}
Since I am passing the driver ID by reference here:
"driver":[ObjectId("5c7c499b555fa13f50c9c248")] , I want to display the driver's attributes from my users collection.
Here is one of my user objects:
{
"_id" : ObjectId("5c7c499b555fa13f50c9c248"),
"password" : "$2a$10$L..Pf44/R7yJfNPdikIObe04aiJaY/e94VSKlFscjgYOe49Y7iwJK",
"email" : "john.smith#yahoo.com",
"firstName" : "John",
"lastName" : "Smith",
"laps" : [],
"__v" : 0,
}
Here is what I tried so far:
db.getCollection('stats').aggregate([
{ $group: {
_id: { map: "$map" }, // replace `name` here twice
laps: { $addToSet: "$lapTime" },
driver:{$addToSet: "$driver"},
count: { $sum: 1 }
} },
{$lookup:
{
from: "users",
localField: "firstName",
foreignField: "lastName",
as: "driver"
}},
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
As a result, I am getting drivers as a empty array.
What I am actually trying to achieve is something like this:
{
"_id" : {
"map" : "France"
},
"laps" : [
"Jonathan Smith":"2:32:34",
"Someone Else":"1:32:29"
],
"count" : 2.0
}
I think this should work:-
db.getCollection('stats').aggregate([
{ $unwind: "$driver" },
{$lookup:
{
from: "users",
localField: "driver",
foreignField: "_id",
as: "driver"
}},
{ $group: {
_id: { map: "$map" }, // replace `name` here twice
laps: { $addToSet:
{
lapTime: "$lapTime",
driverName: "$driver.firstName" + "$driver.lastName"
}
},
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);

Mongodb Aggregate With Deep Nested Array

Hello I tried to querying json data with mongo db with aggregate framework but get stuck, here's the data :
[{
"_id" : ObjectId("5adf2294bc832359aa9bc710"),
"kabupaten" : "Badung",
"jenis_wisata" : [
{
"jenis" : "resto",
"list" : [
{
"nama_resto" : "Warung Nasi Ayam Bu Oki",
"alamat" : "Bukit Jimbaran"
},
{
"nama_resto" : "Warung Pojok Segitiga Emas",
"alamat" : "Bukit Jimbaran"
}
]
},
{
"jenis" : "gunung",
"list" : [
{
"nama_gunung" : "gunung_1",
"alamat" : "alamat_1"
},
{
"nama_gunung" : "gunung_1",
"alamat" : "alamat_1"
}
]
}
]
},{
"_id" : ObjectId("5adf2294bc832359aa9bc711"),
"kabupaten" : "Denpasar",
"jenis_wisata" : [
{
"jenis" : "resto",
"list" : [
{
"nama_resto" : "Warung Nasi Ayam Bu Oki",
"alamat" : "Bukit Jimbaran"
},
{
"nama_resto" : "Warung Pojok Segitiga Emas",
"alamat" : "Bukit Jimbaran"
}
]
},
{
"jenis" : "gunung",
"list" : [
{
"nama_gunung" : "gunung_1",
"alamat" : "alamat_1"
},
{
"nama_gunung" : "gunung_1",
"alamat" : "alamat_1"
}
]
}
]
}]
what I want to achieve is to display the total amount of array "list" inside each element in array "jenis_wisata", here's the expected result that I want to achieve:
[{
"kabupaten" : "Badung",
"jenis_wisata" : [
{
"jenis" : "resto",
"total" : 2
},
{
"jenis" : "gunung",
"total" : 2
}
]
},{
"kabupaten" : "Denpasar",
"jenis_wisata" : [
{
"jenis" : "resto",
"total" : 2
},
{
"jenis" : "gunung",
"total" : 2
}
]
}]
Really appreciate your help here, Thanks
You need $map to transform your inner array and $size to get the length of an array.
db.collection.aggregate([
{
$project: {
kabupaten: 1,
jenis_wisata: {
$map: {
input: "$jenis_wisata",
as: "element",
in: {
jenis: "$$element.jenis",
total: { $size: "$$element.list" }
}
}
}
}
}
])

Mongoose search for element in utmost sub array

My schema is as follows
{
product:[{
structure :[{
version:[{
type:[{
values:[{}]
}]
}]
}]
}]
}
Need to search fields in values array.
Actually I need to update the fields in values array. I'm unable to write query to get sub array values. How can I do that. Kindly let me know, as soon as possible.
You can use this types of query:
// match a one name
db.getCollection('test').find({"product.structure.version.type.values.name":'test'})
// match some value from an array
db.getCollection('test').find({"product.structure.version.type.values.name": {$in: [1, 'test']}})
Last query will find 3 items in the next collection:
/* 1 */
{
"_id" : ObjectId("595b9e17c482ca1b99db23a6"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 1
}
]
}
]
}
]
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("595b9e68f9f2fe8d79ca4c82"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 3
},
{
"name" : 4
},
{
"name" : 1
}
]
}
]
}
]
}
]
}
]
}
/* 3 */
{
"_id" : ObjectId("595b9e71f9f2fe8d79ca4c84"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 5
}
]
}
]
}
]
}
]
}
]
}
/* 4 */
{
"_id" : ObjectId("595ba08af9f2fe8d79ca4cd9"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 6
}
]
}
]
}
]
}
]
}
]
}
/* 5 */
{
"_id" : ObjectId("595ba0f1f9f2fe8d79ca4d00"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : "test"
}
]
}
]
}
]
}
]
}
]
}
UPDATE:
To do an update query, you need to do something like this:
db.getCollection('test').update({"product.structure.version.type.values.name": {$in: [1, 'test']}}, {$set: {"product.$.testProp": 'some test string'}}, {multi: true})

Get Multiple Objects From Array in different Documents MongoDb

My collection coll is
/* 1 */
{
"_id" : ObjectId("566121aa4b88d840eb7d1c50"),
"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"),
"array" : [
{
"id" : 1
},
{
"id" : 2
},
{
"id" : 3
},
{
"id" : 4
}
]
}
/* 2 */
{
"_id" : ObjectId("5661224a4b88d840eb7d1c51"),
"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"),
"array" : [
{
"id" : 1
},
{
"id" : 7
},
{
"id" : 3
},
{
"id" : 5
}
]
}
what i need is to pull objects in array 'array' where
batchCourseId = ObjectId("566122ab94b792fbdf81bcf3")
and 2<array.id<=5
expected output is
/* 1 */
{
"_id" : ObjectId("566121aa4b88d840eb7d1c50"),
"array" : [
{
"id" : 3
},
{
"id" : 4
}
]
}
/* 2 */
{
"_id" : ObjectId("5661224a4b88d840eb7d1c51"),
"array" : [
{
"id" : 3
},
{
"id" : 5
}
]
}
already tried
db.coll.find({"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3")},
{ array: { $elemMatch: { id: { $gt: 2,$lte: 5} } } })
the output is like
/* 1 */
{
"_id" : ObjectId("566121aa4b88d840eb7d1c50"),
"array" : [
{
"id" : 3
}
]
}
/* 2 */
{
"_id" : ObjectId("5661224a4b88d840eb7d1c51"),
"array" : [
{
"id" : 3
}
]
}
close but only the first matching object in array is in result
FYI this only a sample set of data the original data is more complex and big in count
so pls let me know the best practice to do this, performance is also important
thanks in advance
You can use aggregation for achieving the same. A sample is shown below:
db.coll.aggregate(
{$match: {"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3")}},
{$unwind: '$array'},
{$match: {'array.id': { $gt: 2,$lte: 5}}},
{$group: {_id: '$_id', array: {$push : '$array'}}}
)
Result:
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "array" : [ { "id" : 3 }, { "id" : 5 } ] }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "array" : [ { "id" : 3 }, { "id" : 4 } ] }
In MongoDB aggregation $unwind creates Cartesian_product problem so in large data set is good way to avoid $unwind.
Let's check with your example if you use $unwind in aggregation then result looks like this
db.collectionName.aggregate([
{ "$match": { "batchCourseId": ObjectId("566122ab94b792fbdf81bcf3") }},
{ "$unwind": "$array" }
])
so result of above query is :
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 1 } }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 2 } }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 3 } }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 4 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 1 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 7 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 3 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 5 } }
this create multiple documents and in large documents in collections it slow the performance and increase processing time.
Instead of $unwind use $map in aggregation with aggregation-set operator and the query is as below :
db.collection.aggregate([{
"$match": {
"batchCourseId": ObjectId("566122ab94b792fbdf81bcf3")
}
}, {
"$project": {
"array": {
"$setDifference": [{
"$map": {
"input": "$array",
"as": "el",
"in": {
"$cond": {
"if": {
"$and": [{
"$gt": ["$$el.id", 2]
}, {
"$lte": ["$$el.id", 5]
}]
},
"then": "$$el",
"else": false
}
}
}
},
[false]
]
}
}
}])

Resources