How to merge document in mongodb based on an Id - mongodb

Is there a way in mongoDb to merge documents based on id. For eg:
/*1*/{
"id" : "xxxxxx",
"pages" : {
"name" : "Page50-50",
"pageid" : "Page50-50",
"icon" : "/images/cmstemplates/2column.png",
"children" : []
},
"permissions" : [
{
"grpName" : "Admin",
"grants" : [
"View",
"Delete",
"Edit"
]
},
{
"grpName" : "Users",
"grants" : [
"View"
]
}
]
}
/*2*/
{
"id" : "xxxxxx",
"pages" : {
"name" : "AboutUs",
"pageid" : "AboutUs",
"icon" : "/images/cmstemplates/1column.png",
"children" : []
},
"permissions" : [
{
"grpName" : "Student",
"grants" : [
"View",
"Delete"
]
}
]
}
Expected Output:
{
"id" : "xxxxxx",
"pages" : [{
"name" : "Page50-50",
"pageid" : "Page50-50",
"icon" : "/images/2column.png",
"children" : [],
"permissions" : [
{
"grpName" : "Admin",
"grants" : [
"View",
"Delete",
"Edit"
]
},
{
"grpName" : "Users",
"grants" : [
"View"
]
}
]
},{
"name" : "AboutUs",
"pageid" : "AboutUs",
"icon" : "/images/1column.png",
"children" : [],
"permissions" : [
{
"grpName" : "Student",
"grants" : [
"View",
"Delete"
]
}
]
}]
}
The permissions params in the expected output should come inside pages, and pages should group into an array. I used $group and was able to group the pages, but i am not able to figure out how can i add permissions param inside pages. I am using mongodb 3.2.
Thanks.

Use $group
db.getCollection('Collection').aggregate([{
$group: {
"_id": "$_id",
"pages": {
$push: "$pages"
}
}
}])

Related

$lookup nested external id after $group

I want to replace the external user ids with the real user properties after $lookup, my data:
"comments" : [
{
"user_Id" : ObjectId("aaa"),
"content" : "aaaa",
"rep" : [
{
"user_Id" : ObjectId("bbb"),
"comment" : "bbbb",
},
{
"user_Id" : ObjectId("ccc"),
"comment" : "cccc",
}
]
},
{
"user_Id" : ObjectId("ddd"),
"content" : "ddd",
"rep" : [ ]
}
]
User collection:
"users" : [
{
"_id" : ObjectId("aaa"),
"name" : "user1",
"email" : "test1#test.com",
},
{
"_id" : ObjectId("bbb"),
"username" : "user2",
"email" : "test2#test.com",
}
]
What i want to archieve:
"comments" : [
{
"user" : {
"_id" : ObjectId("aaa"),
"name" : "user1",
"email" : "test1#test.com",
}
"content" : "aaaa",
"rep" : [
{
"userId" : {
"_id" : ObjectId("bbb"),
"username" : "user2",
"email" : "test2#test.com",
},
"comment" : "bbbb",
},
{
"user" : {
"_id" : ObjectId("aaa"),
"name" : "user1",
"email" : "test1#test.com",
},
"comment" : "cccc",
}
]
},
{
"user" : {
"_id" : ObjectId("bbb"),
"username" : "user2",
"email" : "test2#test.com",
},
"content" : "ddd",
"rep" : [ ]
}
]
Right now i managed to get my user info from the external id but i'm going crazy trying to get the user object inside the replies too, i tried to group after grouping but nothing to do, this is what i did:
db.pages.aggregate([
{
$match: { _id: ObjectId('5db599f3fffdee1c822269e0b3') }
},
{
$project: {
comments: 1,
}
},
{ $unwind: '$comments' },
{
$lookup:
{
from: 'users',
localField: 'comments.user_Id',
foreignField: '_id',
as: 'us'
}
},
{ $unwind: '$us' },
{
$group: {
_id: {
user: {
id: '$us._id',
name: '$us.username',
email: '$us.email',
},
comments: {
comment: '$comments.comment',
rep: '$comments.rep'
},
}
}
}
]).pretty()

How to filter a nested/lower subdocument

Say if I have a result like this:
{
"_id" : ObjectId("5b722d06c23b5f2bd0329a41"),
"name" : "test",
"item" : {
"_id" : ObjectId("5b722d07c23b5f2bd0329a53"),
"display_name" : "",
"image" : "http://via.placeholder.com/700x500/ffffff/000000/?text=No%20Image&",
"is_private" : true,
"details" : [
{
"is_private" : false,
"type" : "text",
"_id" : ObjectId("5b722d06c23b5f2bd0329a44"),
"title" : "Name",
"content" : "",
"order" : 0
},
{
"is_private" : false,
"type" : "text",
"_id" : ObjectId("5b722d06c23b5f2bd0329a43"),
"title" : "Price",
"content" : "",
"order" : 1
},
{
"is_private" : false,
"type" : "text",
"_id" : ObjectId("5b722d06c23b5f2bd0329a42"),
"title" : "Company",
"content" : "",
"order" : 2
}
],
"tags" : [ ],
"__v" : 0
}
}
and I want to filter by item.details.is_private, how should I do this? I want to return all properties of item but filter out any item.details.is_private if it is true
I am currently projecting it as:
{
"$project": {
name: 1,
item: 1
}
}
but am not sure how to implement $filter in this setting
You can try using $addsFields with $filter aggregation
db.collection.aggregate([
{ "$addFields": {
"item.details": {
"$filter": {
"input": "$item.details",
"as": "detail",
"cond": {
"$eq": [ "$$detail.is_private", true ]
}
}
}
}}
])

MongoDB Query array and return only father an childs that contains the value

I have the following Menus Collection on MongoDB. It has values like Permission and Submenu.
{
"_id" : ObjectId("5b42c5c8a80e160a146822e1"),
"HasSubmenu" : "false",
"Icon" : "fas fa-home",
"MenuCode" : "PaginaInicial",
"MenuName" : "Página Inicial",
"MenuState" : "paginainicial",
"Permission" : 1
"Submenu" : [ ],
},
{
"_id" : ObjectId("5b42c5c8a80e160a146822e2"),
"HasSubmenu" : "true",
"Icon" : "far fa-file-alt",
"MenuCode" : "FormulariosApoio",
"MenuName" : "Formularios de Apoio",
"MenuState" : "",
"Permission" : 1
"Submenu" : [
{
"MenuState" : "tiposassociados",
"MenuName" : "Tipos Associados",
"MenuCode" : "TiposAssociados",
"Icon" : "fas fa-tags",
"HasSubmenu" : false,
"Permission" : 1
},
{
"MenuState" : "naturalidades",
"MenuCode" : "Naturalidade",
"MenuName" : "Naturalidade",
"Icon" : "fas fa-globe-asia",
"HasSubmenu" : false,
"Permission" : 4
},
{
"MenuState" : "nacionalidades",
"MenuCode" : "Nacionalidade",
"MenuName" : "Nacionalidade",
"Icon" : "fas fa-globe-americas",
"HasSubmenu" : false,
"Permission" : 4
},
]
}
I need help returning, only the Menus and Submenus that has the value { "Permission" : 1 }, evend if the Menus, don`t have any Submenus
Tried this one but it doens`t ssm to work.
db.getCollection("menus").find({
$and: [
{"Submenu" : { $elemMatch : {"Permission": 1}}},
{"Permission": 1},
]
});
You can try with $filter aggregation
db.collection.aggregate([
{ "$match": { "Permission": 1 } },
{ "$addFields": {
"Submenu": {
"$filter": {
"input": "$Submenu",
"as": "submenu",
"cond": {
"$eq": [ "$$submenu.Permission", 1 ]
}
}
}
}}
])

Create an unique list of multiple arrays inside a subdocument.

I would like to create an unique list of array values inside a subdocument.
Document:
{
"_id" : ObjectId("5aee0e3c059638093b69c8b3"),
"firstname" : "John",
"lastname" : "Doe",
"websites" : [
{
"_id" : ObjectId("123"),
"key" : "website2",
"url" : "www.xxx.com",
"tags" : [
"php",
"python",
"java"
]
},
{
"_id" : ObjectId("456"),
"key" : "website2",
"url" : "www.yyy.com",
"tags" : [
"java",
"php"
]
},
{
"_id" : ObjectId("789"),
"key" : "website3",
"url" : "www.zzz.com",
"tags" : [
"java",
"html",
"css"
]
}
]
}
Expected output:
{
"_id" : ObjectId("5aee0e3c059638093b69c8b3"),
"firstname" : "John",
"lastname" : "Doe",
"unique_tags": [
"java",
"php",
"python",
"html",
"css",
],
"websites" : [
{
"_id" : ObjectId("123"),
"key" : "website2",
"url" : "www.xxx.com",
"tags" : [
"php",
"python",
"java"
]
},
{
"_id" : ObjectId("456"),
"key" : "website2",
"url" : "www.yyy.com",
"tags" : [
"java",
"php"
]
},
{
"_id" : ObjectId("789"),
"key" : "website3",
"url" : "www.zzz.com",
"tags" : [
"java",
"html",
"css"
]
}
]
}
It looks like Mongo has a distinct functionality, but this does not work inside an aggregate query (right?!).
Also tried to unwind on websites.tags and use the addToSet functionality but it also hasn't the right output.
Any ideas?
You can try below aggregation:
db.col.aggregate([
{
$addFields: {
unique_tags: {
$reduce: {
input: {
$concatArrays: {
$map: {
input: "$websites",
as: "website",
in: "$$website.tags"
}
}
},
initialValue: [],
in: { $setUnion : ["$$value", "$$this"]}
}
}
}
}
])
To flatten an array of arrays (websites -> tags) you can use $map with $concatArrays. Then you'll get an array of all tags from all websites. To get only unique values you can use $reduce with $setUnion (which drops duplicates).

Querying subdocument inside a subdocument of a collection in Mongodb

I have the collection named companies as below.
I want to query the url corresponding to C1S1category1 . I do not know which companyName it belongs and which catergories it belongs to.
Please can you let me know what query I need to use in Mongoshell to query the document having catergoryname as C1S1category1
{"companyName": "C1",
"url": "www.com1",
"categories" : [
{"SlNo" : 1,
"url" : "www.com1",
"subcategories" : [
{
"CatergoryName":"C1S1category1",
"Url" : "www.com3"
},
{
"CatergoryName":"C1S1category2",
"Url" : "www.com3"
}
]
},
{
"SlNo" : 2,
"url" : "www.com1",
"subcategories" : [
{
"CatergoryName":"C1S2category1",
"Url" : "www.com3"
},
{
"CatergoryName":"C1S2category2",
"Url" : "www.com3"
}
]
}
]
},
{"companyName": "C2",
"url": "www.com21",
"categories" : [
{"SlNo" : 1,
"url" : "www.com22",
"subcategories" : [
{
"CatergoryName":"C2S1category1",
"Url" : "www.com23"
},
{
"CatergoryName":"C2S1category2",
"Url" : "www.com23"
}
]
},
{
"SlNo" : 2,
"url" : "www.com1",
"subcategories" : [
{
"CatergoryName":"C2S2category1",
"Url" : "www.com23"
},
{
"CatergoryName":"C2S2category2",
"Url" : "www.com23"
}
]
}
]
}
You need to use $elemMatch to get required output as following:
db.collection.find({
"categories": {
$elemMatch: {
subcategories: {
$elemMatch: {
CatergoryName: "C1S1category1"
}
}
}
}
},{"categories.$":1,"companyName":1,"url":1}).pretty()