mongodb map data in one collection - mongodb

My intention is to map the reference column based on the id. This is my data in one collection.
[{
"title": "A",
"reference": ["2"],
"id": "1",
"author": ["Doraemon"]
},{
"title": "B",
"reference": [],
"id": "2",
"author": ["Naruto"]
}]
Does anyone can help me how to make it like this
[{
"title": "A",
"reference": ["B"],
"id": "1",
"author": ["Doraemon"]
},{
"title": "B",
"reference": [],
"id": "2",
"author": ["Naruto"]
}]

You can do self lookup
db.collection.aggregate([
{
"$lookup": {
"from": "collection",
"localField": "reference",
"foreignField": "id",
"as": "selfJoin"
}
},
{
$addFields: {
reference: "$selfJoin.title"
}
},
{
$project: {
selfJoin: 0
}
}
])
Working Mongo playground

Related

MongoDB: Graphlookup nested documents only returns single document in aggregation

I'm trying the MongoDB aggregation framework to work with nested documents but having trouble returning the expected output, specifically in the $graphLookup stage. In a non-nested schema, it correctly looks up all the documents as defined in the options and returns all. But in a nested one, it only returns one. I have tried $unwind and $replaceRoot as answered here but now it does not work. It would be more understandable through code so here are the samples.
Non-nested document (fileSystem does not count)
db={
"fileSystem": [
{
"_id": "a",
"label": "Root",
"children": [
"b",
],
},
{
"_id": "b",
"label": "Nested folder 1",
"children": [
"c",
"d",
"e"
],
"parent": "a"
},
{
"_id": "c",
"label": "Nested File 1.1",
"parent": "b"
},
{
"_id": "d",
"label": "Nested File 1.2",
"parent": "b"
},
]
}
// Aggregation Query
db.fileSystem.aggregate([
{
"$match": {
"_id": "a"
}
},
{
"$graphLookup": {
"from": "fileSystem",
"startWith": "$children",
"connectFromField": "children",
"connectToField": "_id",
"as": "nest",
"depthField": "level",
"maxDepth": 1
}
},
])
// correct and expected result
[
{
"_id": "a",
"children": [
"b"
],
"label": "Root",
"nest": [
{
"_id": "b",
"children": [
"c",
"d",
"e"
],
"label": "Nested folder 1",
"level": NumberLong(0),
"parent": "a"
},
{
"_id": "d",
"label": "Nested File 1.2",
"level": NumberLong(1),
"parent": "b"
},
{
"_id": "c",
"label": "Nested File 1.1",
"level": NumberLong(1),
"parent": "b"
}
]
}
]
Nested document and query
db={
"fileSystem": [
{
pp: [
{
"_id": "a",
"label": "Root",
"children": [
"b",
],
},
// ... same as previous
]
}
]
}
// Aggregation Query
db.fileSystem.aggregate([
{
"$unwind": "$pp"
},
{
"$replaceRoot": {
"newRoot": "$pp"
}
},
{
"$match": {
"_id": "a"
}
},
{
"$graphLookup": {
"from": "fileSystem",
"startWith": "$pp.children",
"connectFromField": "pp.children",
"connectToField": "pp._id",
"as": "nest",
"depthField": "level",
}
},
])
// incorrect result
[
{
"_id": "a",
"children": [
"b"
],
"label": "Root",
"nest": []
}
]
Expected: https://mongoplayground.net/p/A4yDGUHka58
Bugged: https://mongoplayground.net/p/ZlQyDBrYSZr
$graphLookup searches the collection given in from for matching documents. It uses each document in the pipeline as a starting point, but it does not search for, and will not return documents from the pipline.
In the sample data there is there is only 1 document, so the best you'll get in that case is for the next array to contain the original document.
Playground

Join multiple collections in MongoDB

Greetings amigo i have one question related joining multiple collection in MongoDb
i have collection schema something like below
Posts Collection
{
"type": "POST_TYPE",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"post_id": "63241dffb0f6770c23663230",
"likes": 50
}
Post Types: 1. Event
{
"date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"venue": "Some Place",
"lat": "null",
"long": "null",
}
Post Types: 2. Poll
{
"created_date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"question": "Question??????",
"poll_opt1": "Yes",
"poll_opt2": "No",
"poll_opt1_count": "5",
"poll_opt2_count": "2"
}
now i have to join Post collection with respective collection e.g.
"post_id" to Event::_id or Poll::_id with condition to Post::type
i have tried aggregation but it does not gave expected output.
i am trying to get output something like below
[
{
"type": "event",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"post_id": {
"date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"venue": "Some Place",
"lat": "null",
"long": "null"
},
"likes": 50
},
{
"type": "poll",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"post_id": {
"created_date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"question": "Question??????",
"poll_opt1": "Yes",
"poll_opt2": "No",
"poll_opt1_count": "5",
"poll_opt2_count": "2"
},
"likes": 50
}
]
is there any efficient way to achieve this or better MongoDb schema to manage these types of records?
You can try something like this, using $facet:
db.posts.aggregate([
{
"$facet": {
"eventPosts": [
{
"$match": {
type: "event"
},
},
{
"$lookup": {
"from": "events",
"localField": "post_id",
"foreignField": "_id",
"as": "post_id"
}
}
],
"pollPosts": [
{
"$match": {
type: "poll"
},
},
{
"$lookup": {
"from": "poll",
"localField": "post_id",
"foreignField": "_id",
"as": "post_id"
}
}
]
}
},
{
"$addFields": {
"doc": {
"$concatArrays": [
"$pollPosts",
"$eventPosts"
]
}
}
},
{
"$unwind": "$doc"
},
{
"$replaceRoot": {
"newRoot": "$doc"
}
},
{
"$addFields": {
"post_id": {
"$cond": {
"if": {
"$eq": [
{
"$size": "$post_id"
},
0
]
},
"then": {},
"else": {
"$arrayElemAt": [
"$post_id",
0
]
}
}
}
}
}
])
We do the following, in the query:
Perform two $lookups for the different post_type within $facet. This unfortunately will increase, with the different values of post_type.
Then we combine all the arrays obtained from $facet, using $concatArray.
Then we unwind the concatenated array, and bring the nested document to the root using $replaceRoot.
Finally, for post_id we pick the first array element if it exists, to match the desired output.
Playground link.

MongoDB query: aggregate with "findOne"

I'm trying to make a query on MongoDB 3.4 where I add a field for one specific element of an array. Example of the object:
{
"_id": 1,
"people": [
{
"name": "Jhon Smith",
"age": "30",
"state": "NY"
},{
"name": "Clint Mercer",
"age": "50",
"state": "NY"
},{
"name": "Walter Smith",
"age": "40",
"state": "WI"
}
]
}
And I want to make a query where I'll add to this document an attribute with the first person with "Smith" in it's name. Example:
{
"_id": 1,
"people": [
{
"name": "Jhon Smith",
"age": "30",
"state": "NY"
},{
"name": "Clint Mercer",
"age": "50",
"state": "NY"
},{
"name": "Walter Smith",
"age": "40",
"state": "WI"
}
],
"firstSmith": {
"name": "Jhon Smith",
"age": "30",
"state": "NY"
}
}
I already have the _id of the document I want, but I can't understand how to make a query like this. I'm trying using aggregate with "$match" for the id and "$addFields" after, but I can't make a query that works for this field to find exactly what I want. I think it would be similar to the "findOne" query, but I can't find anything that works on "$addFields".
Obs: I DON'T want the "firstSmith" to be an array with just one "people" inside, I want it as is in the example.
I'd appreciate some help with this one.
$match - to filter the relevant document
$filter with $regexMatch - to filter people array by the name property
arrayElemAt - to get only the first element of above array
$addFields - to add new field with value from above result
db.collection.aggregate([
{
"$match": {
"_id": 1
}
},
{
"$addFields": {
"firstSmith": {
"$arrayElemAt": [
{
"$filter": {
"input": "$people",
"cond": {
"$regexMatch": {
"input": "$$this.name",
"regex": "Smith"
}
}
}
},
0
]
}
}
}
])
Working example

Performing Repeated filtering in mongodb

I want to get the names of people who work in bmw and use these names as filter to find and return the documnents containing the field "car". So the end result must be the last two documents given in this example.
[
{"_id": "235", "name": "indu", "dob": "31/4/15", "company": "bmw"},
{"_id": "236", "name": "prith", "dob": "01/4/98", "company": "bmw"},
{"_id": "237", "name": "rames", "dob": "07/4/00", "company": "renault"},
{"_id": "238", "name": "indu", "salary": "10,000", "car": "yes", "married": "yes"},
{"_id": "239", "name": "prith", "salary": "80,000", "car": "yes", "children": "no"}
]
I appreciate your help, Thanks in advance
You want to use $exists
names = db.collection.distinct{"name", {"company": "bmw"})
db.collection.find({"car": {"$exists": true}, "name": {"$in": names}})
You can also do it in 1 aggregation call although I would not recommend it as It's less efficient.
db.collection.aggregate([
{
"$match": {
"company": "bmw"
}
},
{
$lookup: {
from: "this_collection",
localField: "name",
foreignField: "name",
as: "roots"
}
},
{
"$unwind": "$roots"
},
{
"$replaceRoot": {
"newRoot": "$roots"
}
},
{
"$match": {
"car": {"$exists": true}
}
}
])

Mongo Aggregate Combine Two Documents

Once I've unwound a sub-document array, how do I put it back together with all the original root fields?
Consider the following Tasks data set:
[
{
"_id": "5e95bb1cf36c0ab3247036bd",
"name": "Task A",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": "5e117e5cd90de7187b000d87"
},
{
"_id": "5e95bb30f36c0ab3247036be",
"name": "Task B1",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": "5e117e5cd90de7187b000d87",
"parent": "5e95bb1cf36c0ab3247036bd"
},
{
"_id": "5e95bb35f36c0ab3247036bf",
"name": "Task B2",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": "5e117e5cd90de7187b000d87",
"parent": "5e95bb1cf36c0ab3247036bd"
}
]
So, then I run $graphLookup to get the parent task and populate it's children and then $unwind it and populate the creator field:
[
{
"$match": {
"parent": {
"$exists": false
}
}
},
{
"$graphLookup": {
"from": "tasks",
"startWith": "$_id",
"connectFromField": "_id",
"connectToField": "parent",
"as": "children"
}
},
{
"$unwind": {
"path": "$children"
}
},
{
"$lookup": {
"from": "users",
"localField": "children.creator",
"foreignField": "_id",
"as": "children.creator"
}
},
{
"$unwind": {
"path": "$children.creator"
}
}
]
Which returns the following documents:
[
{
"_id": "5e95bb1cf36c0ab3247036bd",
"name": "Task A",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": "5e117e5cd90de7187b000d87",
"children": [
{
"_id": "5e95bb30f36c0ab3247036be",
"name": "Task B1",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": {
"name": "Jack Frost"
},
"parent": "5e95bb1cf36c0ab3247036bd"
}
]
},
{
"_id": "5e95bb1cf36c0ab3247036bd",
"name": "Task A",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": "5e117e5cd90de7187b000d87",
"children": [
{
"_id": "5e95bb35f36c0ab3247036bf",
"name": "Task B2",
"org": "5e95b9894a0aa0b30dfcbc0b",
"creator": {
"name": "Bill Nye"
},
"parent": "5e95bb1cf36c0ab3247036bd"
}
]
},
]
Lastly, I need to merge all of these duplicate documents back together and join the $children. This is the part I can't figure out. Below is some junk I'm trying but it seems messy to have to specifically list every property.
Is there a better way to combine multiple (mostly) matching docs?
[
...
{
"$group": {
"_id": "$_id",
"name": {
"$mergeObjects": "$properties"
},
"watchers": {
"$addToSet": "$watchers"
},
"assignees": {
"$addToSet": "$assignees"
},
"org": {
"$addToSet": "$$ROOT.org"
},
"children": {
"$push": "$children"
}
}
}
]
Answering my own question here, the best solution I can find is to specify each property but pass it the $first operator. This will ensure that the original value will be passed through.
{
$group: {
_id: '$_id',
name: {$first: '$name'},
org: {$first: '$org'},
creator: {$first: '$creator'},
children: {$push: '$children'}
}
}