Mongo: fetch data as data tree [duplicate] - arrays

I'm working in a REST api with ExpressJS and Mongo and I have a collection with N quantity of levels.
So to solve this problem I'm using an recursive table (or collection) in mongo where a field is the id and every register has a parent_id which is at the same level as it's childs.
To explain better this, here is an E-R representation
So as you se, mongo will save the data like this json (accounts level 0 has null parent)
[
{ "id": "45TYYU", "parent_id": null, "name":"account 1", "type": 1, "category": 1 },
{ "id": "45TYYXT", "parent_id": "45TYYU", "name":"account 2", "type": 1, "category": 1 },
{ "id": "45TYYPZ", "parent_id": "45TYYU", "name":"account 3", "type": 1, "category": 1 },
{ "id": "45TYYPZRE", "parent_id": "45TYYPZ", "name":"account 4", "type": 1, "category": 1 },
{ "id": "45TYYPZSX", "parent_id": "45TYYPZ", "name":"account 5", "type": 1, "category": 1 },
{ "id": "45TYYPZGP", "parent_id": "45TYYXT", "name":"account 6", "type": 1, "category": 1 }
]
account 2 and account 3 are children of account 1, while account 4 and account 5 are children of account tree and account 6 is child of account 2 ... but every register is at the same logical level only identifying through parent_id.
so I need to transform this data into a GET method to restructure it like this:
[
{
"id": "45TYYU",
"parent_id": null,
"name":"account 1",
"type": 1,
"category": 1,
"children": [
{
"id": "45TYYXT",
"parent_id": "45TYYU",
"name":"account 2",
"type": 1,
"category": 1,
"children": [
{ "id": "45TYYPZGP", "parent_id": "45TYYXT", "name":"account 6", "type": 1, "category": 1 }
]
},
{
"id": "45TYYPZ",
"parent_id": "45TYYU",
"name":"account 3",
"type": 1,
"category": 1,
"children": [
{ "id": "45TYYPZRE", "parent_id": "45TYYPZ", "name":"account 4", "type": 1, "category": 1 },
{ "id": "45TYYPZSX", "parent_id": "45TYYPZ", "name":"account 5", "type": 1, "category": 1 }
]
}
]
},
{
"id": "45TFJK",
"parent_id": null,
"name":"account 7",
"type": 1,
"category": 1,
"children": [
{
"id": "47HJJT",
"parent_id": "45TFJK",
"name":"account 8",
"type": 1,
"category": 1
},
{
"id": "47YHJU",
"parent_id": "45TFJK",
"name":"account 8",
"type": 1,
"category": 1
}
]
}
]
Yes... the parents level 0 has null parent_id and I want to put it's children inside an array called "children" and then send like this in the GET response to my UI
What is the best way to do this in expressJS?
Is there a library or component out there that allows me to do this?
Thank you

You can use $graphLookup and other useful array operators,
$match filter that records only have parent_id is null
$graphLookup to get child records and depth number in depthField level
$unwind deconstruct children array and allow to not remove empty children
$sort by depth level field level in descending order
$group by id field and reconstruct children array
db.collection.aggregate([
{ $match: { parent_id: null } },
{
$graphLookup: {
from: "collection",
startWith: "$id",
connectFromField: "id",
connectToField: "parent_id",
depthField: "level",
as: "children"
}
},
{
$unwind: {
path: "$children",
preserveNullAndEmptyArrays: true
}
},
{ $sort: { "children.level": -1 } },
{
$group: {
_id: "$id",
parent_id: { $first: "$parent_id" },
name: { $first: "$name" },
type: { $first: "$type" },
category: { $first: 1 },
children: { $push: "$children" }
}
},
$addFields now find the nested level children and allocate to its level,
$reduce to iterate loop of children array.
initialize default field level default value is -1, presentChild is [], prevChild is [] for the conditions purpose
$let to initialize fields:
prev as per condition if both level are equal then return prevChild otherwise return presentChild
current as per condition if both level are equal then return presentChild otherwise []
in to return level field and prevChild field from initialized fields
presentChild $filter children from prev array and return, merge current objects with children array using $mergeObjects and concat with current array of let using $concatArrays
$addFields to return only presentChild array because we only required that processed array
{
$addFields: {
children: {
$reduce: {
input: "$children",
initialValue: { level: -1, presentChild: [], prevChild: [] },
in: {
$let: {
vars: {
prev: {
$cond: [
{ $eq: ["$$value.level", "$$this.level"] },
"$$value.prevChild",
"$$value.presentChild"
]
},
current: {
$cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.presentChild", []]
}
},
in: {
level: "$$this.level",
prevChild: "$$prev",
presentChild: {
$concatArrays: [
"$$current",
[
{
$mergeObjects: [
"$$this",
{
children: {
$filter: {
input: "$$prev",
as: "e",
cond: { $eq: ["$$e.parent_id", "$$this.id"] }
}
}
}
]
}
]
]
}
}
}
}
}
}
}
},
{
$addFields: {
id: "$_id",
children: "$children.presentChild"
}
}
])
Playground

#turivishal Im using same schema in backend nodejs im getting only show the
null object not for a parent child relation using same aggregration
this.tickets.aggregate([
{
$match: {
parent_id: null
}
},
{
$graphLookup: {
from: "collection",
startWith: "$id",
connectFromField: "id",
connectToField: "parent_id",
depthField: "level",
as: "children"
}
},
{
$unwind: {
path: "$children",
preserveNullAndEmptyArrays: true
}
},
{
$sort: {
"children.level": -1
}
},
{
$group: {
_id: "$id",
parent_id: {
$first: "$parent_id"
},
name: {
$first: "$name"
},
type: {
$first: "$type"
},
category: {
$first: 1
},
children: {
$push: "$children"
}
}
},
{
$addFields: {
children: {
$reduce: {
input: "$children",
initialValue: {
level: -1,
presentChild: [],
prevChild: []
},
in: {
$let: {
vars: {
prev: {
$cond: [
{
$eq: [
"$$value.level",
"$$this.level"
]
},
"$$value.prevChild",
"$$value.presentChild"
]
},
current: {
$cond: [
{
$eq: [
"$$value.level",
"$$this.level"
]
},
"$$value.presentChild",
[]
]
}
},
in: {
level: "$$this.level",
prevChild: "$$prev",
presentChild: {
$concatArrays: [
"$$current",
[
{
$mergeObjects: [
"$$this",
{
children: {
$filter: {
input: "$$prev",
as: "e",
cond: {
$eq: [
"$$e.parent_id",
"$$this.id"
]
}
}
}
}
]
}
]
]
}
}
}
}
}
}
}
},
{
$addFields: {
children: "$children.presentChild"
}
}
]).then((result) => {
console.log('test',result);
// callback(result);
}).catch((error) => {
callback(error);
});
output:
[
{
_id: '45TYYU',
parent_id: null,
name: 'account 1',
type: 1,
category: 1,
children: []
},
{
_id: '45TYYUA',
parent_id: null,
name: 'account 1',
type: 1,
category: 1,
children: []
}
]

Related

Mongo DB query to match a field1 and loop thru another field2 and get output as a single array with all fields of field2

Need help with mongo db query
Mondo db query - search for parents with state good and children with state bad or missing. output should be an array of all the children with state bad or missing from parents with good state
Below is the JSON list
[
{
"name": "parent-a",
"status": {
"state": "good"
},
"children": [
"child-1",
"child-2"
]
},
{
"name": "child-1",
"state": "good",
"parent": "parent-a"
},
{
"name": "child-2",
"state": {},
"parent": "parent-a"
},
{
"name": "parent-b",
"status": {
"state": "good"
},
"children": [
"child-3",
"child-4"
]
},
{
"name": "child-3",
"state": "good",
"parent": "parent-b"
},
{
"name": "child-4",
"state": "bad",
"parent": "parent-b"
},
{
"name": "parent-c",
"status": {
"state": "bad"
},
"children": [
"child-5",
"child-6"
]
},
{
"name": "child-5",
"state": "good",
"parent": "parent-c"
},
{
"name": "child-6",
"state": "bad",
"parent": "parent-c"
}
]
Expected output
"children": [
{
"name": "child-2",
"state": {}
},
{
"name": "child-4",
"state": "bad"
}
]
Any inputs would be appreciated. Thanks in advance :)
One option is to use $lookup* for this:
db.collection.aggregate([
{$match: {state: {$in: ["bad", {}]}}},
{$lookup: {
from: "collection",
localField: "parent",
foreignField: "name",
pipeline: [
{$match: {"status.state": "good"}}
],
as: "hasGoodParent"
}},
{$match: {"hasGoodParent.0": {$exists: true}}},
{$project: {name: 1, state: 1, _id: 0}}
])
See how it works on the playground example
*If your mongoDB version is lower than 5.0 you need to change the syntax a bit. Drop the localField and foreignField of the $lookup and replace with let and equality match on the pipeline
Here is an approach doing this all without a "$lookup" stage as performance usually suffers when involved. Basically we match all relevant children and parents and we group by the child id. if it has a parent (which means the parent has a "good" state, and a "child" which means the child has a "bad/{}" state then it's matched).
You should make sure you have the appropriate indexes to support the initial query.
Additionally I would personally recommend adding a boolean field on each document to mark wether it's a parent or a child. right now we have to use the field structure based on your input to mark this type but I would consider this a bad practice.
Another thing we did not discuss which doesn't seem possible from the current structure is recursion, can a child have children of it's own? Just some things to consider
db.collection.aggregate([
{
$match: {
$or: [
{
$and: [
{
"status.state": "good"
},
{
parent: {
$exists: false
}
},
{
"children.0": {
$exists: true
}
}
]
},
{
$and: [
{
"state": {
$in: [
"bad",
null,
{}
]
}
},
{
parent: {
$exists: true
}
}
]
}
]
}
},
{
$unwind: {
path: "$children",
preserveNullAndEmptyArrays: true
}
},
{
$addFields: {
isParent: {
$cond: [
{
$eq: [
null,
{
$ifNull: [
"$parent",
null
]
}
]
},
1,
0
]
}
}
},
{
$group: {
_id: {
$cond: [
"$isParent",
"$children",
"$name"
]
},
hasParnet: {
$sum: "$isParent"
},
hasChild: {
$sum: {
$subtract: [
1,
"$isParent"
]
}
},
state: {
"$mergeObjects": {
$cond: [
"$isParent",
{},
{
state: "$state"
}
]
}
}
}
},
{
$match: {
hasChild: {
$gt: 0
},
hasParnet: {
$gt: 0
}
}
},
{
$group: {
_id: null,
children: {
$push: {
name: "$_id",
state: "$state.state"
}
}
}
}
])
Mongo Playground

How to reverse $unwind or re-assemble after $lookup?

I´ve been trying to reverse $unwind in nested array. Please, if you could help me it would be great. Thanks in advance.
Here are the details:
checklists collection, this collection has steps and each step has many areas, and I'd like to lookup to fill the area by id. I did it but I cannot reverse $unwind.
{
"steps": [{
"name": "paso1",
"description": "paso1",
"estimated_time": 50,
"active": true,
"areas": [{
"area_id": "60b6e728c44f0365c0d547d6"
}, {
"area_id": "60b6e7a2c44f0365c0d547d8"
}]
}, {
"name": "paso2",
"description": "o",
"estimated_time": 7,
"active": true,
"areas": [{
"area_id": "60b6e76ac44f0365c0d547d7"
}]
}, {
"name": "paso2",
"description": "l",
"estimated_time": 7,
"active": true,
"areas": [{
"area_id": "60b6e728c44f0365c0d547d6"
}]
}],
"name": "prueba",
"description": "prueba",
"type": "prueba",
"active": true,
"updated_at": {
"$date": "2021-06-02T23:56:02.232Z"
},
"created_at": {
"$date": "2021-06-01T22:44:57.114Z"
},
"__v": 0
}
area collection
{
"_id":"60b6e706c44f0365c0d547d5"
"name": "Development",
"short_name": "DEV",
"description": "Development area",
"updated_at": {
"$date": "2021-06-02T02:03:50.383Z"
},
"created_at": {
"$date": "2021-06-02T02:03:50.383Z"
},
"__v": 0,
"active": true
}
My aggregation
db.checklists.aggregate([
{
"$unwind": "$steps"
},
{
"$unwind": "$steps.areas"
},
{
"$lookup": {
"from": "areas",
"let": {
"area_id": {
"$toObjectId": "$steps.areas.area_id"
}
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$area_id"
]
}
}
}
],
"as": "convertedItems"
}
},
{
"$group": {
"_id": "$steps.name",
"root": {
"$first": "$$ROOT"
},
"items": {
"$push": {
"$mergeObjects": [
"$steps.areas",
{
"$arrayElemAt": [
"$convertedItems",
0
]
}
]
}
},
}
},
{
"$addFields": {
"values": {
"$reduce": {
"input": "$items",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
{
"$cond": [
{
"$in": [
"$$this.area_id",
"$$value.area_id"
]
},
[],
[
"$$this"
]
]
}
]
}
}
}
}
},
{
"$addFields": {
"root.steps.areas": "$values"
}
},
{
"$replaceRoot": {
"newRoot": "$root"
}
},
{
"$group": {
"_id": "$_id",
"root": {
"$first": "$$ROOT"
},
"steps": {
"$push": "$steps"
}
}
},
{
"$addFields": {
"root.steps": "$steps"
}
},
{
"$replaceRoot": {
"newRoot": "$root"
}
},
{
"$project": {
"convertedItems": 0
}
}
])
I don´t get to form this output:
{
"steps": [{
"name": "paso1",
"description": "paso1",
"estimated_time": 50,
"active": true,
"areas": [{
"_id": "60b6e728c44f0365c0d547d6",
"name":"Development",
..... //join or lookup
}, {
"_id": "60b6e7a2c44f0365c0d547d8",
"name":"Development",
..... //join or lookup
}]
}],
"name": "prueba",
"description": "prueba",
"type": "prueba",
"active": true,
"updated_at": {
"$date": "2021-06-02T23:56:02.232Z"
},
"created_at": {
"$date": "2021-06-01T22:44:57.114Z"
},
"__v": 0
}
Thank you very much!
$unwind deconstruct steps array
$lookup with areas collection pass area_id in let
$match to check is _id in area_ids after converting to string
$project to show required fields
$group by _id and reconstruct the steps array and pass your required fields
db.checklists.aggregate([
{ $unwind: "$steps" },
{
$lookup: {
from: "areas",
let: { area_id: "$steps.areas.area_id" },
pipeline: [
{
$match: {
$expr: { $in: [{ $toString: "$_id" }, "$$area_id"] }
}
},
{ $project: { name: 1 } }
],
as: "steps.areas"
}
},
{
$group: {
_id: "$_id",
steps: { $push: "$steps" },
name: { $first: "$name" },
description: { $first: "$description" },
type: { $first: "$type" },
active: { $first: "$active" },
updated_at: { $first: "$updated_at" },
created_at: { $first: "$created_at" },
__v: { $first: "$__v" }
}
}
])
Playground

MongoDB $push in Array of Array Error: No value exists in scope for the shorthand property 'elements'

I wish to add data into Elements but get error. How to solve this mongodb/typescript problem?
Attempt 1 Error: No value exists in scope for the shorthand property 'elements'. Either declare one or provide an initializer.ts(18004)
Attempt 1
async add<T extends { id: string; parentID: string, elementNum: number, name: string, link: string}>(collectionName: string, args: T) {
const db = await this.getDB();
const collection = db.collection(collectionName);
return new Promise((resolve, reject) => {
collection.updateOne({ id: args.id }, {$push: {elements[args.elementNum]: {id: uuid(), name: args.name, link: args.link, elements: [] }}}, (err, res) => {
if (err) {
reject(err);
}
resolve(res);
});
});
}
Attempt 2
changed the following
collection.updateOne({ id: args.id }, {$push: {elements: {id: uuid(), name: args.name, link: args.link, elements: [] }}},
Attempt 2 results in Null added in the end
{
"id": "1",
"name": "wk1",
"iconFile": "icon.png",
"elements": [
[
{
"id": "2",
"name": "element2",
"link": "https",
"elements": [
{
"id": "1",
"name": "element1",
"link": "https:"
}
]
}
],
[
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
{
"id": "4",
"name": "w",
"link": "http:/"
}
]
}
],
[
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
{
"id": "4",
"name": "w",
"link": "http://"
}
]
},
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
{
"id": "4",
"name": "w",
"link": "http://www."
}
]
}
],
null,
]
}
What I want to achieve is the following
{
"id": "1",
"name": "wk1",
"iconFile": "icon.png",
"elements": [
[
{
"id": "2",
"name": "element2",
"link": "https",
"elements": [
{
"id": "1",
"name": "element1",
"link": "https:"
}
]
},
{
"id": "newid",
"name": "newname",
"link": "newlink"
"elements":[]
}
],
[
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
.......
]
}
Demo - https://mongoplayground.net/p/Dnmg3lL2891
Use - $[]
The all positional operator $[] indicates that the update operator
should modify all elements in the specified array field.
The $[] operator has the following form:
{ <update operator>: { "<array>.$[]" : value } }
db.collection.update({ _id: 1, "add.id": "1"},
{ $push: { "add.$[].elements": { id: "3", name: "a", link: "" } } })
Demo to push array instead of object - https://mongoplayground.net/p/dh3NSutIv4-
db.collection.update({ _id: 1, "add.id": "1"},
{ $push: { "add.$[].elements": [{ id: "3", name: "a", link: "" }] } })
const args = {};
args.elementNum = 0;
const update = {
[`add.$[].elements.${args.elementNum}`]: {
a: 1
}
};
console.log(update);
//collection.updateOne({ id: args.id }, update); // use like this

Mongo DB $look up Method for Fields in Arrays instead of Collections

I have a user document with the following structure:
{
"_id": {
"$oid": "5e636c552b872f00178033bf"
},
"finance": {
"expenditure": [
{
"status": true,
"_id": {
"$oid": "5e636d442b872f00178033d4"
},
"amount": {
"$numberInt": "900"
},
"category": "Coffee"
},
{
"status": true,
"_id": {
"$oid": "5e636d492b872f00178033d5"
},
"amount": {
"$numberInt": "1000"
},
"category": "Coffee"
},
{
"status": true,
"_id": {
"$oid": "5e636d532b872f00178033d6"
},
"amount": {
"$numberInt": "3000"
},
"category": "Sport"
},
{
"status": true,
"_id": {
"$oid": "5e636d572b872f00178033d7"
},
"amount": {
"$numberInt": "1000"
},
"category": "Sport"
},
],
"customcategories": [
{
"budget": {
"$numberInt": "200"
},
"_id": {
"$oid": "5e636c552b872f00178033c7"
},
"title": "Sport"
},
{
"budget": {
"$numberInt": "100"
},
"_id": {
"$oid": "5e636c552b872f00178033c8"
},
"title": "Coffee"
}
]
}
}
My previos command is this one (you don't have to mind the status and the currentdate) :
User.aggregate([
{
$match: {
_id: req.user._id
}
},
{
$unwind: "$finance.expenditure"
},
{
$match: {
"finance.expenditure.status": true
}
},
{
$sort: {
"finance.expenditure.currentdate": -1
}
},
{
$group: {
_id: "$finance.expenditure.category",
amount: {
$sum: "$finance.expenditure.amount",
}
}
},
{
$project: {
_id: 0,
category: "$_id",
amount: 1
}
}
])
The Result looks like this :
{
"expenditure": [
{
"amount": 1900,
"category": "Coffee"
},
{
"amount": 4000,
"category": "Sport"
}
]
}
I would like to add the my grouped elements the budget from the associated "customcategory".
So that it looks like this :
{
"expenditure": [
{
"amount": 1900,
"category": "Coffee",
"budget" : 100
},
{
"amount": 4000,
"category": "Sport",
"budget" : 200
}
]
}
I tried several things but nothing works of the $lookup method worked for me.
I hope some can help me :)
give this pipeline a try:
db.collection.aggregate([
{
$match: { _id: ObjectId("5e636c552b872f00178033bf") }
},
{
$unwind: "$finance.expenditure"
},
{
$match: { "finance.expenditure.status": true }
},
{
$sort: { "finance.expenditure.currentdate": -1 }
},
{
$group: {
_id: "$finance.expenditure.category",
amount: { $sum: "$finance.expenditure.amount"},
categories: { $first: '$finance.customcategories' }
}
},
{
$project: {
_id: 0,
category: "$_id",
amount: 1,
budget: {
$arrayElemAt: [
{
$map: {
input: {
$filter: {
input: '$categories',
cond: { $eq: ['$$this.title', '$_id'] }
}
},
in: '$$this.budget'
}
},
0
]
}
}
}
])
https://mongoplayground.net/p/adsWInz3wgY
Try this one:
User.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(req.user._id)
}
},
{
$sort: {
"finance.expenditure.currentdate": -1
}
},
{
$unwind: "$finance.expenditure"
},
{
$unwind: "$finance.customcategories"
},
{
$match: {
"finance.expenditure.status": true
}
},
{
$group: {
_id: "$finance.expenditure.category",
amount: {
$addToSet: "$finance.expenditure"
},
customcategories: {
$addToSet: "$finance.customcategories"
}
}
},
{
$project: {
_id: 0,
"amount": {
$sum: "$amount.amount"
},
"category": "$_id",
"budget": {
$sum: {
$let: {
vars: {
budget: {
$filter: {
input: "$customcategories",
cond: {
$eq: [
"$_id",
"$$this.title"
]
}
}
}
},
in: "$$budget.budget"
}
}
}
}
}
])
//.exec(function(err, result){})
MongoPlayground

how to find duplicate array value on document mongodb

I got question might be many of you can help me.
so I have data on mongodb.
first data
{
"name" : 'david'
contacts : [
{
"name" : 'john',
"phone" : '123456'
},
{
"name" : 'george',
"phone" : '0987654'
}
]
}
second data
{
"name" : 'anita',
"contacts" : [
{
"name" : 'harry',
"phone" : '123456'
},
{
"name" : 'kurita',
"phone" : '323434'
}
]
}
the problem is,
can I query to find data that have duplicate contacts.phone.
so the result whill show like this.
{
"name" : 'david',
"contacts" : [
{
"name" : 'john',
"phone" : '123456'
}
]
}
{
"name" : 'anita',
"contacts" : [
{
"name" : 'harry',
"phone" : '123456'
}
]
}
data john and anita will show because they have similar data on contacts.phone
sorry for my english btw,
I hope you all understand what I mean.
thank you so much
There are a few steps involved to get the results you need.
We are going to write an aggregate pipeline to get the work done.
First you need to unwind your array values with the following:
{
$unwind: "$contacts"
}
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
This would result into:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"contacts": {
"name": "john",
"phone": "123456"
},
"name": "david"
},
{
"_id": ObjectId("5a934e000102030405000000"),
"contacts": {
"name": "george",
"phone": "0987654"
},
"name": "david"
},
{
"_id": ObjectId("5a934e000102030405000001"),
"contacts": {
"name": "harry",
"phone": "123456"
},
"name": "anita"
},
{
"_id": ObjectId("5a934e000102030405000001"),
"contacts": {
"name": "kurita",
"phone": "323434"
},
"name": "anita"
}
]
This would be much easier for us to group by field.
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation/group/
{
$group: {
_id: {
phone: "$contacts.phone"
},
name: {
$addToSet: "$name"
},
contacts: {
$addToSet: "$contacts.name"
},
count: {
$sum: 1
}
}
}
That gives the following output:
[
{
"_id": {
"phone": "323434"
},
"contacts": [
"kurita"
],
"count": 1,
"name": [
"anita"
]
},
{
"_id": {
"phone": "123456"
},
"contacts": [
"john",
"harry"
],
"count": 2,
"name": [
"david",
"anita"
]
},
{
"_id": {
"phone": "0987654"
},
"contacts": [
"george"
],
"count": 1,
"name": [
"david"
]
}
]
Based on the output we need to match the count greater then 1 like:
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation/match/
{
$match: {
count: {
"$gt": 1
}
}
}
Result is:
[
{
"_id": {
"phone": "123456"
},
"contacts": [
"john",
"harry"
],
"count": 2,
"name": [
"david",
"anita"
]
}
]
The query would look like:
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
db.collection.aggregate([
{
$unwind: "$contacts"
},
{
$group: {
_id: {
phone: "$contacts.phone"
},
name: {
$addToSet: "$name"
},
contacts: {
$addToSet: "$contacts.name"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
"$gt": 1
}
}
}
])
MongoPlayground: https://mongoplayground.net/p/qSvhcYyAcQO
I hope this gives you a small idea what is possible with the aggregation pipeline.
Update / fix
According to your requirements you wish to have 2 objects foreach name that has duplicate contacts then you could use unwind again after the match.
[
{
"_id": {
"phone": "123456"
},
"contacts": [
"harry",
"john"
],
"count": 2,
"name": "david"
},
{
"_id": {
"phone": "123456"
},
"contacts": [
"harry",
"john"
],
"count": 2,
"name": "anita"
}
]
Cheers, Kevin

Resources