lookup compare collection data with array in aggregate result in mongo DB - arrays

i want to compare collection with array in aggregate result
i have following two collection.
chat collection
chat.tags is a array value in reference key come from the tags collection.
"chat": [
{
"id": "test1",
"tags": [
"AAA",
"BBB",
"CCC",
"AAA"
]
},
{
"id": "test2",
"tags": [
"AAA",
"BBB",
"CCC"
]
}
]
tag collection
"tag": [
{
"id": "1234",
"key": "AAA",
"name": "a"
},
{
"id": "1235",
"key": "BBB",
"name": "b"
},
{
"id": "1236",
"key": "CCC",
"name": "c"
},
{
"id": "1237",
"key": "DDD",
"name": "d"
},
]
i want to result that id is "test1" and unique tags in chat collection.
i want to following result using with mongo aggregate.
Is it possible with from, let, pipeline when using lookup?
[
{
"chat": [
{
"id": "test1",
"setTags": [
"AAA",
"BBB",
"CCC"
]
}
],
"tag": [
{
"id": "1234",
"key": "AAA",
"name": "a"
},
{
"id": "1235",
"key": "BBB",
"name": "b"
},
{
"id": "1236",
"key": "CCC",
"name": "c"
}
]
}
]
please help me.

This can be achieved with a simple $lookup, like so:
db.chat.aggregate([
{
$match: {
id: "test1"
}
},
{
$lookup: {
from: "tag",
localField: "tags",
foreignField: "key",
as: "tagDocs"
}
},
{
$project: {
chat: [
{
id: "$id",
setTags: "$tags"
}
],
tag: "$tagDocs"
}
}
])
Mongo Playground
I didn't fully understand what the output structure you want is but it can easily be changed via a different $project stage.
--- EDIT ---
With Mongo's v3.6 $lookup syntax the pipeline remains the same, just the $lookup stage changes:
{
$lookup: {
from: "tag",
let: {
tagKeys: "$tags"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$key",
"$$tagKeys"
]
}
}
}
],
as: "tagDocs"
}
},
Mongo Playground

Related

In MongoDB I am searching in Nested Object Array but the result it is showing not appropriate

I am using collection Name "History" having below data
[
{
"_id": {
"$oid": "634a9d1b269c99e9364e8750"
},
"marks": [
{
"results": [
{
"product": "Abc",
"score": 55
}
]
}
]
},
{
"_id": {
"$oid": "634a9fae269c99e9364e8755"
},
"marks": [
{
"results": [
{
"product": "Abc",
"score": 10
},
{
"product": "Xyz",
"score": 5
}
]
}
]
},
{
"_id": {
"$oid": "634a9fae269c99e9364e8756"
},
"marks": [
{
"results": [
{
"product": "Abc",
"score": 8
},
{
"product": "Xyz",
"score": 7
}
]
}
]
},
{
"_id": {
"$oid": "634a9fae269c99e9364e8757"
},
"marks": [
{
"results": [
{
"product": "Abc",
"score": 7
},
{
"product": "Xyz",
"score": 8
}
]
}
]
}
]
My Fetch command is this..
db.History.findOne({"marks.results.product":"Xyz"})
command executes without an error but it shows wrong results..
{
"_id": {
"$oid": "634a9fae269c99e9364e8755"
},
"marks": [
{
"results": [
{
"product": "Abc",
"score": 10
},
{
"product": "Xyz",
"score": 5
}
]
}
]
}
The 1st object in results (product:"Abc") should not display as its not meet the criteria (Product="Xyz")
please correct me and guaid me how to fetch desired data (objects only meet criteria i.e. Product="Xyz" )
Your sample data here is a collection with 4 documents. 3 of these contain product="Xyz" along with other products. findOne finds one document that has product="Xyz" inside it, and return the document as is (without manipulating it).
What you are requesting is to get back only a part of the document - meaning you want to manipulate the (double) nested array results in the returned answer. You can do it using an aggregation pipeline.
One option is to use $unwind for this
db.collection.aggregate([
{$match: {"marks.results.product": "Xyz"}},
{$limit: 1} // if you want only one document to return
{$unwind: "$marks"},
{$unwind: "$marks.results"},
{$match: {"marks.results.product": "Xyz"}}
])
See how it works on the playground example - unwind
Another option is to $map and $filter:
See how it works on the playground example - filter

MongoDB get only selected elements from objects inside an array

What I have is a collection of documents in MongoDB that have the structure something like this
[
{
"userid": "user1",
"addresses": [
{
"type": "abc",
"street": "xyz"
},
{
"type": "def",
"street": "www"
},
{
"type": "hhh",
"street": "mmm"
},
]
},
{
"userid": "user2",
"addresses": [
{
"type": "abc",
"street": "ccc"
},
{
"type": "def",
"street": "zzz"
},
{
"type": "hhh",
"street": "yyy"
},
]
}
]
If I can give the "type" and "userid", how can I get the result as
[
{
"userid": "user2",
"type": "abc",
"street": "ccc",
}
]
It would also be great even if I can get the "street" only as the result. The only constraint is I need to get it in the root element itself and not inside an array
Something like this:
db.collection.aggregate([
{
$match: {
userid: "user1" , "address.type":"abc"
}
},
{
$project: {
userid: 1,
address: {
$filter: {
input: "$addresses",
as: "a",
cond: {
$eq: [
"$$a.type",
"abc"
]
}
}
}
}
},
{
$unwind: "$address"
},
{
$project: {
userid: 1,
street: "$address.street",
_id: 0
}
}
])
explained:
Filter only documents with the userid & addresess.type you need
Project/Filter only the addresses elements with the needed type
unwind the address array
project only the needed elements as requested
For best results create index on the { userid:1 } field or compound index on { userid:1 , address.type:1 } fields
playground
You should be able to use unwind, match and project as shown below:
db.collection.aggregate([
{
"$unwind": "$addresses"
},
{
"$match": {
"addresses.type": "abc",
"userid": "user1"
}
},
{
"$project": {
"_id": 0,
"street": "$addresses.street"
}
}
])
You can also duplicate the match step as the first step to reduce the number of documents to unwind.
Here is the playground link.
There is a similar question/answer here.

How to remove duplicate values inside a list array in MongoDB?

I have many records in one collection in MongoDB and this is 3 examples to remove only based one QUESTION match criteria.
{
"_id": {
"$oid": "5f0f561256efe82f5082252e"
},
"Item1": false,
"Item2": "",
"Item3": 1,
"Item4": [
{
"Name": "TYPE",
"Value": "QUESTION"
},
{
"Name": "QUESTION",
"Value": "What is your name?"
},
{
"Name": "CORRECT_ANSWER",
"Value": "1"
},
{
"Name": "ANSWER_1",
"Value": "name one"
},
{
"Name": "ANSWER_2",
"Value": "name two"
}
],
"Item5": [
10
],
"Item6": false
}
and another one to compare
{
"_id": {
"$oid": "5f0f561256efe82f5082252c"
},
"Item1": false,
"Item2": "",
"Item3": 2,
"Item4": [
{
"Name": "TYPE",
"Value": "QUESTION"
},
{
"Name": "QUESTION",
"Value": "What is your name?"
},
{
"Name": "CORRECT_ANSWER",
"Value": "1"
},
{
"Name": "ANSWER_1",
"Value": "name one"
},
{
"Name": "ANSWER_2",
"Value": "name two"
}
],
"Item5": [
10
],
"Item6": false
}
the third one :
{
"_id": {
"$oid": "5f0f561256efe82f5082252d"
},
"Item1": false,
"Item2": "",
"Item3": 3,
"Item4": [
{
"Name": "TYPE",
"Value": "QUESTION"
},
{
"Name": "QUESTION",
"Value": "What is your last name?"
},
{
"Name": "CORRECT_ANSWER",
"Value": "1"
},
{
"Name": "ANSWER_1",
"Value": "name one"
},
{
"Name": "ANSWER_2",
"Value": "name two"
}
],
"Item5": [
10
],
"Item6": false
}
What I'm trying here is to make query with aggregation approach and I only want to focus on Item4 for exactly ("Name": "QUESTION") and the value (the question) for identifying the duplication.
The idea is to looking for duplication in the the question itself only ("What is your name?") in our example here. and I don't want to specify witch question because there are long list of them.
I'm looking just for the duplicated questions no mater what is the question look like.
I used the following approach but still I cannot narrow down the output to be only related to question and its value in order to delete the duplicate in the another step.
db.collections.aggregate([{ $unwind: "$Item4" }, {$group: { _id: { QUESTION: "$Item4.Name.4", Value: "$Item4.Value.4" }}}]).pretty()
I'm executing from mongo shell directly.
The following aggregation will list all the documents (the _ids) which have the duplicates of "Item4.Value" for the condition "Item4.Name": "QUESTION".
db.test.aggregate( [
{
$unwind: "$Item4"
},
{
$match: { "Item4.Name": "QUESTION" }
},
{
$group: {
_id: { "Item4_Value": "$Item4.Value" },
ids: { $push: "$_id" }
}
},
{
$match: { $expr: { $gt: [ { $size: "$ids" }, 1 ] } }
}
] )
It works! thanks a lot. I add it to the rest of code as below :
db.test.find().count()
const duplicatesIds = [];
db.test.aggregate( [
{
$unwind: "$Item4"
},
{
$match: { "Item4.Name": "QUESTION" } //here is the trick...to filter the array to pass only the condition "Item4.Name": "QUESTION".
},
{
$group: {
_id: { "Item4_Value": "$Item4.Value" },
ids: { $push: "$_id" }
}
}
],
{
allowDiskUse: true
}
).forEach(function (doc) {
doc.ids.shift();
doc.ids.forEach(function (dupId) {
duplicatesIds.push(dupId);
})
});
printjson(duplicatesIds);
db.test.remove({_id:{$in:duplicatesIds}})
db.test.find().count()

Mongodb group and sum two arrays of similar data from aggregate facet

I am running queries that target different levels of social data and have got this working with some fairly large aggregate pipelines which then run inside a $facet.
The results of the $facet section come out with two arrays I then want to combine. eg.
{
"first": [
{
"_id": {
"name": "one",
"_id": "1"
},
"date": [
"2017-09-07T00:00:00.000Z"
],
"data": [
1000
]
},
{
"_id": {
"name": "two",
"_id": "2"
},
"date": [
"2017-09-07T00:00:00.000Z"
],
"data": [
2000
]
}
],
"second": [
{
"_id": {
"name": "1",
"_id": "one"
},
"date": [
"2017-09-07T00:00:00.000Z"
],
"data": [
1000
]
},
{
"_id": {
"name": "two",
"_id": "2"
},
"date": [
"2017-09-07T00:00:00.000Z"
],
"data": [
2000
]
}
]
}
I would like to group these results together and add up the totals to end up with
[ {
"_id": {
"name": "1",
"_id": "one"
},
"date": [
"2017-09-07T00:00:00.000Z"
],
"data": [
2000
]
},{
"_id": {
"name": "2",
"_id": "two"
},
"date": [
"2017-09-07T00:00:00.000Z"
],
"data": [
4000
]
}]
I have a query that groups the individual responses to get them to this level but is missing something that allows me to merge across the two arrays. I have experimented with unwinding the first and second array but haven't been able to find a solution that combines the data across both arrays.
Any help, tips or knowledge greatly appreciated.
Use $concatArrays to merge arrays from $facets output followed by $group with $arrayElemAt to pick the first element for $first and $sum accumulator.
[
{
"$project": {
"combine": {
"$concatArrays": [
"$first",
"$second"
]
}
}
},
{
"$unwind": "$combine"
},
{
"$group": {
"_id": "$combine._id",
"date": {
"$first": {
"$arrayElemAt": [
"$combine.date",
0
]
}
},
"data": {
"$sum": {
"$arrayElemAt": [
"$combine.data",
0
]
}
}
}
}
]

Filter for array type of field in loopback

I am using loopback v3 with mongodb database and implementing filter for array type of field.
inq operator is not working.
I have an array of object like below
[
{
"name":"name1",
"title": "title1",
"category": ["a", "b","c"]
},
{
"name":"name2",
"title": "title2",
"category": ["x", "y","z"]
},
{
"name":"name3",
"title": "title3",
"category": ["b", "d","e"]
}
]
now i want a list where category containing "b"
So i am using below filter method
filter: {where:{category:{inq:["b"]}}}
I think inq does n't work for this case.it gives empty response.
Output : [ ]
how can i get my desired output.
Desired output:
[
{
"name":"name1",
"title": "title1",
"category": ["a", "b","c"]
},
{
"name":"name3",
"title": "title3",
"category": ["b", "d","e"]
}
]
below is my properties
"properties": {
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"category": {
"type": [
"string"
]
}
},
Please suggest.
Thanks
for me the above scenario works fine. Although in your code the array closing brackets should be ] instead of }, just pointing out something I found in your code.
How did you setup your model for this?
"properties": {
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"category": {
"type": [
"string"
]
}
},
Does your model properties look like this ?
"role": {
"type": "array",
"default": [
"patient"
]
}
let filter = {role:{in:['doctor']}}
this.find({
where: filter
}, cb);

Resources