MongoDB aggregation lookup, push output of both collections into sub objects - mongodb

https://mongoplayground.net/p/xQp-y1iXUtZ
There are 2 collections:
profile
subs
After a lookup, the foreign collection is returned as an array. Since there can be only one element in that array (a sub can only have one profile), we take the first one.
Now, I would like to "push" the "subs" collection into an object as well. There are a lot of fields in "subs".
This is what I have:
[
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"_id": "1",
"f1": "f1",
"f2": "f1",
"f3": "f1",
"f4": "f1",
"username": "gk"
},
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"_id": "2",
"f1": "f1",
"f2": "f3",
"f3": "f4",
"f4": "f5",
"username": "gk"
}
]
This is what I am looking for:
[
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"SUBS": {
"_id": "1",
"f1": "f1",
"f2": "f1",
"f3": "f1",
"f4": "f1",
"username": "gk"
}
},
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"SUBS": {
"_id": "2",
"f1": "f1",
"f2": "f3",
"f3": "f4",
"f4": "f5",
"username": "gk"
}
}
]
Essentially the contents of the "local" collection also as an object.

You add projection before lookup,
$project to create a field SUBS and set $$ROOT as the value that is the root document
$lookup to join profile collection, pass SUBS.username as localField, and set PROFILE0 as as value, and we don't need $unset stage
$set same as you did
db.subs.aggregate([
{
$project: {
_id: 0,
SUBS: "$$ROOT"
}
},
{
$lookup: {
from: "profile",
localField: "SUBS.username",
foreignField: "name",
as: "PROFILE0"
}
},
{
$set: {
"PROFILE0": { $arrayElemAt: ["$PROFILE0", 0] }
}
}
])
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

Aggregate (Reflexive Join) in MongoDB

I have the following data in collection "sampleorgchart":
Eventually we want to use the "Relationship" field in a match/filter statement, but first I just want to get some demo query to work with the hierarchy.
I am trying the following:
db.sampleorgchart2.aggregate([
{
$graphLookup: {
from: "sampleorgchart2",
startWith: "$To",
connectFromField: "To",
connectToField: "From",
as: "myList",
maxDepth: 2,
depthField: "depth"
}
}
])
It runs, but return 0 items in each of the "myList" fields:
// 1
{
"_id": ObjectId("6180023c68600000010000f5"),
"From": "John Doe",
"Relationship": "is CEO of ",
"To ": "Sample Company",
"myList": [ ]
}
Is something wrong with my query? I would like to see the connected rows in "myList".
For further practice, I copied the employee data from this page, and got that query working.
https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/#examples
So by changing the collection name and the field names, I don't understand why it won't work with my sampleorgchart2 collection. I got the query that page working, then reversed it to go down the hierarchy instead of up the hierarchy:
db.simpleorgchart.aggregate([
{
$graphLookup: {
from: "simpleorgchart",
startWith: "$reportsTo",
connectFromField: "name",
connectToField: "reportsTo",
as: "myReportingHierarchy",
maxDepth: 2,
depthField: "depth"
}
}
])
Sample results:
// 1
{
"_id": "1",
"name": "Dev",
"reportsTo": null,
"myReportingHierarchy": [
{
"_id": "1",
"name": "Dev",
"reportsTo": null,
"depth": NumberLong("0")
},
{
"_id": "2",
"name": "Eliot",
"reportsTo": "Dev",
"depth": NumberLong("1")
},
{
"_id": "3",
"name": "Ron",
"reportsTo": "Eliot",
"depth": NumberLong("2")
},
{
"_id": "4",
"name": "Andrew",
"reportsTo": "Eliot",
"depth": NumberLong("2")
}
]
}

MongoDB: How to merge original record back after lookup

I have the following collections which I am using a $lookup to bring together:
{
"organizations": [
{
"_id": 1,
"name": "foo",
"users": [1,2]
},
{
"_id": 2,
"name": "bar",
"users": [1]
}
],
"users": [
{
"_id": 1,
"name": "john1 smith"
},
{
"_id": 2,
"name": "bob johnson"
}
]
}
The query works fine:
[{
"$lookup": {
"from": "users",
"localField": "users",
"foreignField": "_id",
"as": "members"
}
},
{
"$unwind": "$members"
},
{
"$group": {
"_id": "$_id",
"original": { "$first": "$$ROOT" },
"members": {
"$push": "$members"
}
}
}
]
however, the resulting organizations records don't return all of their properties without adding the original prop which gives me back a nesting of the original organization:
{
"_id": 1,
"original": {
"_id": 1,
"name": "foo",
"users": [
1,
2
],
"members": {
"_id": 1,
"name": "john1 smith"
}
},
"members": [
{
"_id": 1,
"name": "john1 smith"
},
{
"_id": 2,
"name": "bob johnson"
}
]
}
I'm trying to get everything in the original prop back into the root along with the new members array.

mongodb distinct query values

I have the following mongodb documents:
{
"_id": "",
"name": "example1",
"colors": [
{
"id": 1000000,
"properties": [
{
"id": "1000",
"name": "",
"value": "green"
},
{
"id": "2000",
"name": "",
"value": "circle"
}
]
} ]
}
{
"_id": "",
"name": "example2",
"colors": [
{
"id": 1000000,
"properties": [
{
"id": "1000",
"name": "",
"value": "red"
},
{
"id": "4000",
"name": "",
"value": "box"
}
]
} ]
}
I would like to get distinct queries on the value field in the array where id=1000
db.getCollection('product').distinct('colors.properties.value', {'colors.properties.id':{'$eq': 1000}})
but it returns all values in the array.
The expected Result would be:
["green", "red"]
There are a lot of way to do.
$match eliminates unwanted data
$unwind de-structure the array
$addToSet in $group gives the distinct data
The mongo script :
db.collection.aggregate([
{
$match: {
"colors.properties.id": "1000"
}
},
{
"$unwind": "$colors"
},
{
"$unwind": "$colors.properties"
},
{
$match: {
"colors.properties.id": "1000"
}
},
{
$group: {
_id: null,
distinctData: {
$addToSet: "$colors.properties.value"
}
}
}
])
Working Mongo playground

How to group the documents which I just unwinded in mongodb?

This is the initial document. I applied $unwind on "colors" and another $unwind on "colors.sizes". But now am not able to group them back. How is that done?
{
"_id": ObjectId("5ef838255c959771c46fc917"),
"created": ISODate("2017-03-04T18:30:00.000Z"),
"updated": ISODate("2020-06-28T00:49:23.000Z"),
"status": "active",
"productId": "3828",
"tags": {
"colors": ["BEIGE", "BLACK", "BLUE", "GREEN", "GREY", "LIGHT BEIGE", "LIGHT PINK", "NAVY", "PINK", "WHITE"],
"size": ["39", "39.5", "40", "40.5", "41", "41.5", "42", "42.5", "43", "43.5", "44", "44.5", "45", "45.5", "46"],
"category": ["SHOES", "SNEAKERS", "MENS SHOES & ACCESSORIES"],
"price": ["3400.00", "1360.00", "2040.00", "1700.00", "45575.00"],
"season": ["SS(2020)", "AW(2019)", "SS(2019)", "AW(2018)", "SS(2017)", "AW(2016)", "SS(2016)", "SS(2015)"],
"hash": ["ACTIVE", "ANKLE", "BLUSH", "COLLAR", "COLOUR", "DESIGNER", "FOIL", "GOLD-TONE", "LACE-UP", "LEATHER", "LOW", "LOW TOP", "ORIGINAL", "PADDED", "ROUND", "ROUND TOE", "RUBBER", "SIGNATURE", "TONAL"]
},
"name": "ORIGINAL ARCHILLES LOW SNEAKER",
"description": "",
"brand": "COMMON PROJECTS",
"styleCode": "220036200",
"availableOnline": true,
"colors": [{
"images": [],
"_id": ObjectId("5ef838255c959771c46fc918"),
"colorId": "1",
"color": "WHITE",
"hexCode": "#FFFFFF",
"status": "active",
"sizes": [{
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc919"),
"sizeId": "1",
"neo": "0220003258813",
"size": "39",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}, {
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc91a"),
"sizeId": "2",
"neo": "0220111220849",
"size": "39.5",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}, {
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc91b"),
"sizeId": "3",
"neo": "0220004218564",
"size": "40",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}]
}, ]
}
db.getCollection('products').aggregate([
{$match:{"productId":"3828"}},
{$unwind:"$colors"},
{$unwind:"$colors.sizes"},
{
$lookup: {
from:"inventories",
localField: "colors.sizes.neo",
foreignField: "sku",
as:"colors.sizes.stores"
}
}
])
The opposite operator of $unwind is $group
Explanation
We $group by productId + colors[*]._id fields grouping root, colors and sizes separately.
Note: You can define each variable separately like this:
created: {$first:"$created"},
updated: {$first:"$updated"},
...
OR
use aggregation variable $$ROOT which contains entire document fields
We "merge" each variables into single object and move into root level.
Note: If we don't use $replaceRoot, the data structure will looks like this:
{
_id: "...",
root: {
_id: "...",
...
colors:{
...
sizes:[...]
}
}
}
Note: Unfortunately, MongoDB doesn't allow merge subkeys like this:
{
$mergeObjects:[
"$root",
{"colors":"$colors"},
{"colors.sizes":"$sizes"} <-- Not allowed
]
}
So we have to use $objectToArray + $map + $arrayToObject operators
$objectToArray - transforms object into array
{"hello": "world"} ---> [{"k":"hello", "v":"world"}]
$map - iterates array and transforms array content.
In this case, we replace `sizes` field with `sizes` from the 1st step
$objectToArray - transforms "special" array into object
[{"k":"hello", "v":"world"}] ---> {"hello": "world"}
Now we group only by productId and group colors and root values separately
We finish merging variables and transform into desired object (same as 2nd step)
Add these steps into your pipeline:
{
$group: {
"_id": {
productId: "$productId",
colors: "$colors._id"
},
"root": {
$first: "$$ROOT"
},
"colors": {
$first: "$colors"
},
"sizes": {
$push: "$colors.sizes"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{
"colors": {
$arrayToObject: {
$map: {
input: { $objectToArray: "$colors" },
as: "color",
in: {
k: "$$color.k",
v: {
$cond: [
{ $eq: [ "$$color.k", "sizes" ] },
"$sizes",
"$$color.v"
]
}
}
}
}
}
}
]
}
}
},
{
$group: {
"_id": "$_id",
"root": {
$first: "$$ROOT"
},
"colors": {
$push: "$colors"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{ "colors": "$colors" }
]
}
}
}
MongoPlayground