Related
I have a database as below.
[
{
"title": "Data 1",
"slug": "data-1",
"sub_docs": [
{
"name": "Sub Doc 1",
"sub_slug": "sub-doc-1",
"urls": [
"https://example.com/path-1",
]
},
{
"name": "Sub Doc 2",
"sub_slug": "sub-doc-2",
"urls": [
"https://example.com/path-2"
]
},
{
"name": "Sub Doc 3",
"sub_slug": "sub-doc-3",
"urls": [
"https://example.com/path-3",
]
}
]
}
]
When I give data-1 and sub-doc-2 as input, I want to get the following result.
{
"title": "Data 1",
"sub_doc_title": "Sub Doc 2",
"urls": [
"https://example.com/path-2"
],
"prev": {
"name": "Sub Doc 1",
"sub_slug": "sub-doc-1"
},
"next": {
"name": "Sub Doc 3",
"sub_slug": "sub-doc-3"
}
}
I'm a bit of a beginner at MongoDB.
How can I do this query? Do you have an idea?
That's a tough query for beginner, although I still consider myself a novice so maybe there's a better/easy way.
Here's one way you could do it.
db.collection.aggregate([
{
"$match": {
"slug": "data-1" // given argument
}
},
{
"$set": {
"reduceOut": {
"$reduce": {
"input": "$sub_docs",
"initialValue": {
"sub_doc_title": null,
"urls": [],
"prev": null,
"next": null
},
"in": {
"$cond": [
{
"$eq": [
"$$this.sub_slug",
"sub-doc-2" // given argument
]
},
{
"$mergeObjects": [
"$$value",
{
"sub_doc_title": "$$this.name",
"urls": "$$this.urls"
}
]
},
{
"$cond": [
{ "$eq": [ "$$value.sub_doc_title", null ] },
{
"$mergeObjects": [
"$$value",
{
"prev": {
"name": "$$this.name",
"sub_slug": "$$this.sub_slug"
}
}
]
},
{
"$cond": [
{ "$eq": [ "$$value.next", null ] },
{
"$mergeObjects": [
"$$value",
{
"next": {
"name": "$$this.name",
"sub_slug": "$$this.sub_slug"
}
}
]
},
"$$value"
]
}
]
}
]
}
}
}
}
},
{
"$replaceWith": {
"$mergeObjects": [
{
"title": "$title"
},
"$reduceOut"
]
}
}
])
Try it on mongoplayground.net.
Thanks rickhg12hs for your answer. I also found a solution to this question.
Here is my answer;
db.collection.aggregate([
{
"$match": {
"slug": "data-1"
}
},
{
"$addFields": {
"sub_doc_index": {
"$indexOfArray": [
"$sub_docs.sub_slug",
"sub-doc-2"
]
}
}
},
{
"$project": {
"title": 1,
"sub_doc_title": {
"$arrayElemAt": [
"$sub_docs.name",
"$sub_doc_index"
]
},
"urls": {
"$arrayElemAt": [
"$sub_docs.urls",
"$sub_doc_index"
]
},
"prev": {
"$cond": {
"if": {
"$eq": [
{
"$subtract": [
"$sub_doc_index",
1
]
},
-1
]
},
"then": {},
"else": {
"name": {
"$arrayElemAt": [
"$sub_docs.name",
{
"$subtract": [
"$sub_doc_index",
1
]
}
]
},
"slug": {
"$arrayElemAt": [
"$sub_docs.sub_slug",
{
"$subtract": [
"$sub_doc_index",
1
]
}
]
}
},
},
},
"next": {
"name": {
"$arrayElemAt": [
"$sub_docs.name",
{
"$add": [
"$sub_doc_index",
1
]
}
]
},
"slug": {
"$arrayElemAt": [
"$sub_docs.sub_slug",
{
"$add": [
"$sub_doc_index",
1
]
}
]
}
}
}
},
])
Playground:https://mongoplayground.net/p/YUV_fReyGsr
I have following query. I need to combine the result 2 by 2. Meaning I need to combine facet "1","2" as a result and facet "3","4" as another result. It's guaranteed that the number of facet will be even. Also, each pair of facet should get at most one record(it might not matter)
db.collection.aggregate([
{
"$facet": {
"1": [
{
$match: {
"ID": "2"
}
}
],
"2": [
{
$match: {
"array.ID": "2"
}
}
],
"3": [
{
$match: {
"array.ID": "4"
}
}
],
"4": [
{
$match: {
"ID": "4"
}
}
]
}
}
])
The expected result will be
[
{
"1": [
{
"ID": "1",
"array": [
{
"ID": "2",
"attribute1": "456"
},
{
"ID": "3",
"attribute1": "567"
}
],
"attr1": "123"
}
],
"2": [
{
"ID": "4",
"array": [
{
"ID": "5",
"attr1": "456"
}
],
"attr1": "123"
}
]
}
]
I was able to figure this out using $concatArrays operator, along with $project.
Live demo here
Database
[
{
"ID": "1",
"attr1": "123",
"array": [
{
"ID": "2",
"attribute1": "456"
},
{
"ID": "3",
"attribute1": "567"
}
]
},
{
"ID": "4",
"attr1": "123",
"array": [
{
"ID": "5",
"attr1": "456"
}
]
}
]
Query
db.collection.aggregate([
{
"$facet": {
"1": [
{
$match: {
"ID": "2"
}
}
],
"2": [
{
$match: {
"array.ID": "2"
}
}
],
"3": [
{
$match: {
"array.ID": "4"
}
}
],
"4": [
{
$match: {
"ID": "4"
}
}
]
}
},
{
"$project": {
_id: 0,
"1": {
"$concatArrays": [
"$1",
"$2"
]
},
"2": {
"$concatArrays": [
"$3",
"$4"
]
}
}
}
])
Result
[
{
"1": [
{
"ID": "1",
"_id": ObjectId("5a934e000102030405000000"),
"array": [
{
"ID": "2",
"attribute1": "456"
},
{
"ID": "3",
"attribute1": "567"
}
],
"attr1": "123"
}
],
"2": [
{
"ID": "4",
"_id": ObjectId("5a934e000102030405000001"),
"array": [
{
"ID": "5",
"attr1": "456"
}
],
"attr1": "123"
}
]
}
]
Let's consider that I have the following documents (ignoring the _id):
[
{
"Id": "Store1",
"Info": {
"Location": "Store1 Street",
"PhoneNumber": 111
},
"MaxItemsPerShelf": 3,
"Shelf": [
{
"Id": "Shelf1",
"Items": [
{
"Id": "Item1",
"Name": "bananas"
},
{
"Id": "Item2",
"Name": "apples"
},
{
"Id": "Item3",
"Name": "oranges"
}
]
},
{
"Id": "Shelf2",
"Items": [
{
"Id": "Item4",
"Name": "cookies"
},
{
"Id": "Item5",
"Name": "chocolate"
}
]
},
{
"Id": "Shelf3",
"Items": []
}
]
},
{
"Id": "Store3",
"Info": {
"Location": "Store2 Street",
"PhoneNumber": 222
},
"MaxItemsPerShelf": 2,
"Shelf": [
{
"Id": "Shelf4",
"Items": [
{
"Id": "Item6",
"Name": "champoo"
},
{
"Id": "Item7",
"Name": "toothpaste"
}
]
},
{
"Id": "Shelf5",
"Items": [
{
"Id": "Item8",
"Name": "chicken"
}
]
}
]
}
]
Given a specific Shelf.Id I want to get the following result ( Shelf.Id = "Shelf2"):
[{
"Info": {
"Location": "Store1 Street",
"PhoneNumber": 111
},
"ItemsNumber": 2,
"ItemsRemaining": 1
}]
Therefore:
ItemsNumberis the $size of Shelf
and
ItemsRemainingis equal to MaxItemsPerShelf $size of Shelf
also I want to copy the value of the Info to the aggregate output.
How can I accomplish this with aggregate? On my efforts I couldn't pass through an iterator that gets the $size of $Shelf.Items
You can use below aggregation
db.collection.aggregate([
{ "$match": { "Shelf.Id": "Shelf2" }},
{ "$replaceRoot": {
"newRoot": {
"$let": {
"vars": {
"shelf": {
"$filter": {
"input": {
"$map": {
"input": "$Shelf",
"in": {
"Id": "$$this.Id",
"count": { "$size": "$$this.Items" }
}
}
},
"as": "ss",
"cond": { "$eq": ["$$ss.Id", "Shelf2"] }
}
}
},
"in": {
"Info": "$Info",
"ItemsNumber": { "$arrayElemAt": ["$$shelf.count", 0] },
"ItemsRemaining": {
"$subtract": [
"$MaxItemsPerShelf",
{ "$ifNull": [
{ "$arrayElemAt": ["$$shelf.count", 0] },
0
]}
]
}
}
}
}
}}
])
Considering I have the following collection (ignoring documents _id):
Collection
[
{
"Id": "1",
"Level2": [
{
"Id": "1.1",
"Level3": [
{
"Id": "1.1.1",
"Level4": [
{
"Id": "1.1.1.1",
"Status": "a"
},
{
"Id": "1.1.1.2",
"Status": "b"
}
]
},
{
"Id": "1.1.2",
"Level4": [
{
"Id": "1.1.2.1",
"Status": "a"
},
{
"Id": "1.1.2.2",
"Status": "c"
}
]
},
{
"Id": "1.1.3",
"Level4": [
{
"Id": "1.1.3.1",
"Status": "c"
}
]
}
]
}
]
},
{
"Id": "2",
"Level2": [
{
"Id": "2.1",
"Level3": [
{
"Id": "2.1.1",
"Level4": [
{
"Id": "2.1.1.1",
"Status": "a"
}
]
}
]
}
]
}
]
Conditions
I want to query it such as:
Level2.Id: "1.1"
Level2.Level3.Level4.Status $in ["a", "b"]
The $project should:
Group Level2.Level3.Level4 matches by Level2.Level3
Should not include Level2.Level3 if it is an empty array
The expected result should be:
Result 1
[
{
"Id": "1.1.1",
"Level4": [
{
"Id": "1.1.1.1",
"Status": "a"
},
{
"Id": "1.1.1.2",
"Status": "b"
}
]
},
{
"Id": "1.1.2",
"Level4": [
{
"Id": "1.1.2.1",
"Status": "a"
}
]
}
]
Please notice that if current Level2.Id: "2.1" has the value "1.1" instead, the result should be (matches on different documents):
Result 2
[
{
"Id": "1.1.1",
"Level4": [
{
"Id": "1.1.1.1",
"Status": "a"
},
{
"Id": "1.1.1.2",
"Status": "b"
}
]
},
{
"Id": "1.1.2",
"Level4": [
{
"Id": "1.1.2.1",
"Status": "a"
}
]
},
{
"Id": "2.1.1",
"Level4": [
{
"Id": "2.1.1.1",
"Status": "a"
}
]
}
]
How can I accomplish this with an aggregate query?
( filtering different levels and grouping by some level )
Here it is the answer (thank you to #Vijay Rajpurohit for his help on this):
db.collection.aggregate([
{
$unwind: "$Level2"
},
{
$unwind: "$Level2.Level3"
},
{
$unwind: "$Level2.Level3.Level4"
},
{
$match: {
"Level2.Id": "1.1",
"Level2.Level3.Level4.Status": {
$in: [
"a",
"b"
]
}
}
},
{
$group: {
"_id": "$Level2.Level3.Id",
"Messages": {
$push: "$Level2.Level3.Level4"
}
}
}
])
The result of this query is:
[
{
"Messages": [
{
"Id": "1.1.2.1",
"Status": "a"
}
],
"_id": "1.1.2"
},
{
"Messages": [
{
"Id": "1.1.1.1",
"Status": "a"
},
{
"Id": "1.1.1.2",
"Status": "b"
}
],
"_id": "1.1.1"
}
]
Mongo Playground
How to populate in result of aggregated query in monogdb
Array of followedId
var followeduserId = ["abc","efg","xyz","pqr","acd","rts"];
Feeds Recommended
[
{
"feedsId": "feed1",
"userId": "abc"
},
{
"feedsId": "feed1",
"userId": "efg"
}
]
Feeds collection
[
{
"link": "www.yodo.com",
"recommended": [
"abc",
"efg"
],
"title": "This is my feed7",
"topics": [
"topi1",
"topi2",
"topi3",
"topi4"
]
},
{
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"das",
"asd",
"eqw",
"weq"
],
"title": "This is my feed8",
"topics": [
"topi1",
"topi2",
"topi3",
"topi4"
]
}
]
Ran aggregation query
feedsrecommended.aggregate([
{ $match: { userId: { $in: "followersId" }}},
{ $lookup: {
from: "feeds",
localField: "feedsId",
foreignField: "_id",
as: "feedsId"
}},
{ $group: {
"_id": { "feedsId": "$feedsId" },
"count": { "$sum": 1 }
}},
{ $sort: { count: -1 }}
])
result After aggregation
var resultfeeds = [
{
"count": 7,
"id": {
"_id": "feed1",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"xyz",
"pqr",
"acd",
"rts"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi8",
"topi6",
"topi5"
]
}
},
{
"count": 3,
"id": {
"_id": "feed5",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"acd",
"rts"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi2",
"topi3",
"topi4"
]
}
},
{
"count": 3,
"id": {
"_id": "feed6",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"xyz",
"pqr"
],
"title": "This is my feed1",
"topics": [
"topi7",
"topi1",
"topi4",
"topi8"
]
}
},
{
"count": 2,
"id": {
"_id": "feed2",
"link": "www.yodo.com",
"recommended": [
"abc",
"acd",
"rts"
],
"title": "This is my feed1",
"topics": [
"topi7",
"topi6",
"topi8"
]
}
},
{
"count": 2,
"id": {
"_id": "feed7",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi5",
"topi6",
"topi4"
]
}
},
{
"count": 1,
"id": {
"_id": "feed3",
"link": "www.yodo.com",
"recommended": [
"abc",
"asd",
"eqw",
"weq"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi7",
"topi6",
"topi4"
]
}
},
{
"count": 1,
"id": {
"_id": "feed8",
"link": "www.yodo.com",
"recommended": [
"abc",
"das",
"asd",
"eqw",
"weq"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi2",
"topi5",
"topi4"
]
}
}
]
I want to populate topics and recommeded userName and image in the result
topic collection
[
{
"topic_name": "tiger"
},
{
"topic_name": "loin"
}
]
user collection
[
{
"name": "deepa",
"profileImg": "www.com/facebook.jpg"
},
{
"name": "nisa",
"profileImg": "www.com/facebook.jpg"
}
]
My last result should be like this
[
{
"count": 2,
"id": {
"_id": "feed2",
"link": "www.yodo.com",
"recommended": [
{
"_id": "abc",
"name": "deepa",
"profileImg": "www.com/facebook.jpg"
},
{
"_id": "acd",
"name": "sigger",
"profileImg": "www.com/facebook.jpg"
},
{
"_id": "rts",
"name": "buster",
"profileImg": "www.com/facebook.jpg"
}
],
"title": "This is my feed1",
"topics": [
{
"_id": "topi6",
"topic_name": "boolena"
},
{
"_id": "topi7",
"topic_name": "mika"
},
{
"_id": "topi8",
"topic_name": "tika"
}
]
}
}
]
You can try below aggregation in mongodb 3.6 and above
Feedsrecommended.aggregate([
{ "$match": { "userId":{ "$in": followersId }}},
{ "$group": {
"_id": "$feedsId",
"count": { "$sum": 1 }
}},
{ "$lookup": {
"from": "feeds",
"let": { "feedsId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$feedsId" ] }}},
{ "$lookup": {
"from": "topics",
"let": { "topics": "$topics" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$topics" ] } } }
],
"as": "topics"
}},
{ "$lookup": {
"from": "users",
"let": { "recommended": "$recommended" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$recommended" ] } } }
],
"as": "recommended"
}}
],
"as": "feedsId"
}}
])