Get parent to child relation with mongodb aggregation - node.js

I have one collection called "location". in this collection all child and parent collection are stores. now I want to create a query who returns me parent to child comma separated string.
Collection
businessId: { type: mongoose.Schema.Types.ObjectId, ref: 'admin' },
parentId: { type: mongoose.Schema.Types.ObjectId, ref: 'location' },
name: { type: String },
image: { type: String },
imageManipulation: { type: String },
locationColor: [{ range: { type: String }, color: { type: String } }],
area: {},
settings: {},
status: { type: String, enum: [0, 1], default: 1 },
isChild: { type: String, enum: [0, 1] },
parentPosition: { type: String }
In the above collection, you can see parentId field. if the location is a child then it have parentId. if the location is a parent then parentId will null. parent location can N level child location.
Collection Data
[{
"_id": ObjectId("5ce4f84547e90a0b9c3c4763"),
"name": "Test",
"settings": {
"zoom": "3",
"positionX": "69",
"positionY": "69",
"width": "500",
"height": "334"
},
"parentId": null,
"image": "1558509637101.jpg",
"status": "0",
"businessId": ObjectId("5cbd61dc3b56b902284ea388"),
"locationColor": [],
"updatedAt": ISODate("2019-05-22T12:59:26.013Z"),
"createdAt": ISODate("2019-05-22T07:20:37.112Z"),
"__v": 0
},
{
"_id": ObjectId("5ce50caf09359e1b8ccf5c79"),
"name": "Test sub 1",
"settings": {
"zoom": "3",
"positionX": "48",
"positionY": "3",
"width": "500",
"height": "334"
},
"area": "",
"parentId": ObjectId("5ce4f84547e90a0b9c3c4763"),
"image": "1558514863396.jpg",
"status": "0",
"businessId": ObjectId("5cbd61dc3b56b902284ea388"),
"locationColor": [],
"updatedAt": ISODate("2019-05-22T12:59:21.883Z"),
"createdAt": ISODate("2019-05-22T08:47:43.421Z"),
"__v": 0
},
{
"_id": ObjectId("5ce53977e46da33e6cfdd9d1"),
"name": "Test Sub 2",
"settings": {
"zoom": "5",
"positionX": "0",
"positionY": "0",
"width": "500",
"height": "334"
},
"area": "",
"parentId": ObjectId("5ce50caf09359e1b8ccf5c79"),
"image": "1558526327126.jpg",
"businessId": ObjectId("5cbd61dc3b56b902284ea388"),
"locationColor": [],
"updatedAt": ISODate("2019-05-22T11:58:47.147Z"),
"createdAt": ISODate("2019-05-22T11:58:47.147Z"),
"__v": 0
}]
Expected Result
Test, Test sub 1, Test Sub 2
Expected result in JSON
[{
"_id": ObjectId("5ce4f84547e90a0b9c3c4763"),
"name": "Test",
},
{
"_id": ObjectId("5ce50caf09359e1b8ccf5c79"),
"name": "Test, Test sub 1",
},
{
"_id": ObjectId("5ce53977e46da33e6cfdd9d1"),
"name": "Test, Test sub 1, Test Sub 2",
}]

You basically need $graphLookup for recursive loop over the same collection.
db.location.aggregate([
{ "$graphLookup": {
"from": "location",
"startWith": "$parentId",
"connectFromField": "parentId",
"connectToField": "_id",
"as": "parent"
}},
{ "$project": {
"name": {
"$concat": [
"$name",
{ "$reduce": {
"input": "$parent",
"initialValue": "",
"in": { "$concat": [",", "$$this.name", "$$value"] }
}}
]
}
}}
])
Which will output
[
{
"_id": ObjectId("5ce4f84547e90a0b9c3c4763"),
"name": "Test"
},
{
"_id": ObjectId("5ce50caf09359e1b8ccf5c79"),
"name": "Test sub 1,Test"
},
{
"_id": ObjectId("5ce53977e46da33e6cfdd9d1"),
"name": "Test Sub 2,Test sub 1,Test"
}
]
MongoPlayground

Related

Retrieve value from mongoose object after query

I have a query as follows :
await Categories.findOne({slug : _gender} , { _id : 0 , categories :{$elemMatch : {slug : _mainCategory}}})
and the model is this :
const categoriesSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String },
slug: { type: String }, ==> match first param with this
categories: [
{
_id: mongoose.Schema.Types.ObjectId,
title: { type: String },
slug: { type: String }, ==> match the second param with this
categories: [ ==> retrieve this field
{
_id: mongoose.Schema.Types.ObjectId,
title: { type: String },
slug: { type: String },
},
],
},
],
});
_gender and _mainCategory are params.
The output it gives is as follows:
{
"categories": [
{
"id": "12",
"title": "Ayakkabı",
"slug": "kadin-ayakkabi", // ==> this is the { _id : 0 , categories :{$elemMatch : {slug : _mainCategory}} part, one level above this is the part which matches the {slug : _gender}
"image": "https://webizade.com/bm/img/",
"categories": [ // ===> this is the value I want to retrieve after the query
{
"id": "100",
"title": "Topuklu Ayakkabı",
"filter_title": "ayakkabi",
"slug": "kadin-topuklu-ayakkabi-x-g1-c107",
"image": "https://webizade.com/bm/img/"
},
{
"id": "101",
"title": "Spor Ayakkabı",
"filter_title": "ayakkabi",
"slug": "spor-ayakkabi",
"image": "https://webizade.com/bm/img/"
},
{
"id": "102",
"title": "Günlük Ayakkabı",
"filter_title": "ayakkabi",
"slug": "kadin-gunluk-ayakkabi-x-g1-c1352",
"image": "https://webizade.com/bm/img/"
},
{
"id": "103",
"title": "Babet",
"filter_title": "ayakkabi",
"slug": "kadin-babet-x-g1-c113",
"image": "https://webizade.com/bm/img/"
},
{
"id": "104",
"title": "Sandalet",
"filter_title": "ayakkabi",
"slug": "kadin-sandalet-x-g1-c111",
"image": "https://webizade.com/bm/img/"
},
{
"id": "105",
"title": "Terlik",
"filter_title": "ayakkabi",
"slug": "kadin-terlik-x-g1-c110",
"image": "https://webizade.com/bm/img/"
},
{
"id": "144",
"title": "Kar Botu",
"filter_title": "ayakkabi",
"slug": "kar-botu",
"image": "https://webizade.com/bm/img/"
}
]
}
]
}
As far as matching the correct object goes, this is correct. However, I want to retrieve the "categories" array inside the outer categories array after finding the correct object. But I don't know the correct syntax for doing that.
Appreciate any help.

Mongoose/Express population issue

Here are my schemas:
1.The Records schema:
const mongoose = require('mongoose')
const RecordsSchema = new mongoose.Schema({
Title: { type: String, required: true },
postedby: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}],
Author: { type: String, required: true },
ISBN: { type: String, required: true },
Review: { type: String },
SelectedFile: { type: String },
Likes: { type: Number, default: 0 },
Date: { type: Date, default: Date.now() }
});
module.exports = Records = mongoose.model('record', RecordsSchema, 'record');
Here is the The user Schema:
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
username: { type: String },
email: { type: String, required: true, unique: true },
records: [{
type: [mongoose.Schema.Types.ObjectId],
ref: 'record'
}],
password: { type: String, required: true },
Date: { type: Date, default: Date.now(), immutable: true }
});
module.exports = User = mongoose.model('user', userSchema, 'user');
The express route for getting a record:
router.get('/postedby/', (req, res) => {
Records.findOne()
.populate('postedby')
.exec()
.then(post => {
if (!post) {
return res.status(400).json({ msg: 'Add Posts' });
}
else return res.json(post);
}).catch(err => console.error(err))
});
Result of the route:
{
"postedby": [],
"Likes": 0,
"_id": "5fed8c12a4fb2c1e98ef09f6",
"Title": "New Age",
"Author": "Situma Prisco",
"ISBN": "23422",
"SelectedFile": "",
"Review": "",
"Date": "2020-12-31T08:30:10.321Z",
"__v": 0
},
I'm getting a blank Array on the populated user field(posteddby) .
Please help, What am I doing wrong? And yes, i do have a User Logged in
I implemented your code and Schemas, It's work for me, if you stored data correctly in the database, every things will be ok...
note :
please use find({}) instead of findOne() if data is not displayed again for postedby key, Other issues need to be addressed like mongoose version and ..., because your code and schemas is correct
it's result for me with find({}):
[
{
"postedby": [
{
"records": [
"5ff6bc0bd9e7437184b83f76",
"5ff6bc23d9e7437184b83f78",
"5ff6bc85f1045a4f102bc0cd",
"5ff6bca1f1045a4f102bc0ce",
"5ff6bca5f1045a4f102bc0cf",
"5ff6bcb3f1045a4f102bc0d0",
"5ff6bcc7f1045a4f102bc0d1"
],
"_id": "5ff6b81df463322abc7406ec",
"Date": "2021-01-07T07:28:15.654Z",
"email": "a#test.com",
"password": "$2a$12$wJVDysQxbjuRve.hYn/lbO0rskhwj6y8lwDuEWpCwHeNT/V2mybs.",
"__v": 7
}
],
"Likes": 1,
"Date": "2021-01-07T07:47:11.610Z",
"_id": "5ff6bca1f1045a4f102bc0ce",
"Title": "farsi",
"Author": "javid1",
"ISBN": "1",
"Review": "1",
"SelectedFile": "1",
"__v": 0
},
{
"postedby": [
{
"records": [
"5ff6bc0bd9e7437184b83f76",
"5ff6bc23d9e7437184b83f78",
"5ff6bc85f1045a4f102bc0cd",
"5ff6bca1f1045a4f102bc0ce",
"5ff6bca5f1045a4f102bc0cf",
"5ff6bcb3f1045a4f102bc0d0",
"5ff6bcc7f1045a4f102bc0d1"
],
"_id": "5ff6b81df463322abc7406ec",
"Date": "2021-01-07T07:28:15.654Z",
"email": "a#test.com",
"password": "$2a$12$wJVDysQxbjuRve.hYn/lbO0rskhwj6y8lwDuEWpCwHeNT/V2mybs.",
"__v": 7
}
],
"Likes": 1,
"Date": "2021-01-07T07:47:11.610Z",
"_id": "5ff6bca5f1045a4f102bc0cf",
"Title": "farsi",
"Author": "javid1",
"ISBN": "1",
"Review": "1",
"SelectedFile": "1",
"__v": 0
},
{
"postedby": [
{
"records": [
"5ff6bc0bd9e7437184b83f76",
"5ff6bc23d9e7437184b83f78",
"5ff6bc85f1045a4f102bc0cd",
"5ff6bca1f1045a4f102bc0ce",
"5ff6bca5f1045a4f102bc0cf",
"5ff6bcb3f1045a4f102bc0d0",
"5ff6bcc7f1045a4f102bc0d1"
],
"_id": "5ff6b81df463322abc7406ec",
"Date": "2021-01-07T07:28:15.654Z",
"email": "a#test.com",
"password": "$2a$12$wJVDysQxbjuRve.hYn/lbO0rskhwj6y8lwDuEWpCwHeNT/V2mybs.",
"__v": 7
}
],
"Likes": 1,
"Date": "2021-01-07T07:47:11.610Z",
"_id": "5ff6bcb3f1045a4f102bc0d0",
"Title": "riyazi",
"Author": "javid1",
"ISBN": "1",
"Review": "1",
"SelectedFile": "1",
"__v": 0
},
{
"postedby": [
{
"records": [
"5ff6bc0bd9e7437184b83f76",
"5ff6bc23d9e7437184b83f78",
"5ff6bc85f1045a4f102bc0cd",
"5ff6bca1f1045a4f102bc0ce",
"5ff6bca5f1045a4f102bc0cf",
"5ff6bcb3f1045a4f102bc0d0",
"5ff6bcc7f1045a4f102bc0d1"
],
"_id": "5ff6b81df463322abc7406ec",
"Date": "2021-01-07T07:28:15.654Z",
"email": "a#test.com",
"password": "$2a$12$wJVDysQxbjuRve.hYn/lbO0rskhwj6y8lwDuEWpCwHeNT/V2mybs.",
"__v": 7
}
],
"Likes": 1,
"Date": "2021-01-07T07:47:11.610Z",
"_id": "5ff6bcc7f1045a4f102bc0d1",
"Title": "zaban",
"Author": "javid1",
"ISBN": "1",
"Review": "1",
"SelectedFile": "1",
"__v": 0
},
{
"postedby": [
{
"records": [
"5ff6c275964d062f045e93d3",
"5ff6c283964d062f045e93d5"
],
"_id": "5ff6c253964d062f045e93d2",
"Date": "2021-01-07T08:01:21.499Z",
"email": "b#test.com",
"password": "$2a$12$jmHUrTSPwjaVd0VEIpsGauExHNSukHRyWWiJt4UlEgeWLBo8GPDH.",
"__v": 2
}
],
"Likes": 1,
"Date": "2021-01-07T08:01:21.990Z",
"_id": "5ff6c275964d062f045e93d3",
"Title": "zaban",
"Author": "javid1",
"ISBN": "1",
"Review": "1",
"SelectedFile": "1",
"__v": 0
},
{
"postedby": [
{
"records": [
"5ff6c275964d062f045e93d3",
"5ff6c283964d062f045e93d5"
],
"_id": "5ff6c253964d062f045e93d2",
"Date": "2021-01-07T08:01:21.499Z",
"email": "b#test.com",
"password": "$2a$12$jmHUrTSPwjaVd0VEIpsGauExHNSukHRyWWiJt4UlEgeWLBo8GPDH.",
"__v": 2
}
],
"Likes": 1,
"Date": "2021-01-07T08:01:21.990Z",
"_id": "5ff6c283964d062f045e93d5",
"Title": "tttt",
"Author": "javid1",
"ISBN": "1",
"Review": "1",
"SelectedFile": "1",
"__v": 0
}
]

How to insert a list and insert a nested embed list in MongoDB

I have this structure :
{
"_id": "blablabla",
"UUID": "50ED0565C5BF",
"Email": "foobar#bar.ca",
"FirstName": "Bacel",
"LastName": "ELbs",
"ClientID": "sa-sa",
"UserSettings": {
"dateRangeMode": "1",
"skillsMode": "1",
"CalendarViewMode": "One Month"
},
"Filters": [{
"Type": "Calendar",
"Filter": [],
"DefaultFilterID": ""
}, {
"Type": "Calendar",
"desc": "test",
"Filter": [],
"isDefault": false
}]
}
My objective is to first insert one new object to the Filters array and insert multiple items to the Filters.Filter embedded array :
so I'm trying the following approch :
static async saveFilter(UUID, Type, desc, isDefault, aFilters) {
//let objectID = UUID.replace(/-/g, "");
return await user.update({
UUID: UUID
}, {
$push: {
Filters: {
Type: Type,
desc: desc,
isDefault: isDefault
},
"Filters.Filter": ["a","b","c"]
}
});
}
I keep having this error :
Cannot create field 'Filter:' in element {Filters: [ { Type:
"Calendar", Filter: [], DefaultFilterID: "" }, { Type: "Calendar",
desc: "test", isDefault: false } ]}
But I don't want to creat a new field, I simply want to add elements,
desired output is something like :
{
"_id": "blablabla",
"UUID": "50ED0565C5BF",
"Email": "foobar#bar.ca",
"FirstName": "Bacel",
"LastName": "ELbs",
"ClientID": "sa-sa",
"UserSettings": {
"dateRangeMode": "1",
"skillsMode": "1",
"CalendarViewMode": "One Month"
},
"Filters": [{
"Type": "Calendar",
"Filter": [],
"DefaultFilterID": ""
}, {
"Type": "Calendar",
"desc": "test",
"Filter": [],
"isDefault": false
},
{
"Type": "Tower",
"desc": "Tesla",
"Filter": ["a","b","c"],
"isDefault": false
}]
Is this what you're looking for?
(playground)
db.users.update({
UUID: UUID
},
{
$push: {
Filters: {
Type: Type,
desc: desc,
isDefault: isDefault,
"Filter": [
"a",
"b",
"c"
]
}
}
})

How to filter results from collection the $lookup in mongoose

i want to filter the result as the following in mongodb. I use $lookup to populate the result from another collection. Please check my following code
This code below is what i get
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"_id": "5f3a9bbc325a074240a1a815",
"firstname": "test",
"lastname": "test",
"storename": "test",
"gst": "test",
"phoneNumber": 1,
"email": "1#demo.com",
"address1": "test",
"address2": "test",
"city": "test",
"state": "test",
"country": "test",
"zip": "1",
"password": "1",
"usertype": 3,
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0
}
]
},
How to retrieve only unique_SHOP and zip from source listing.I want result like the one below with one or more fields
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"zip": "1",
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
}
]
},
The query i use
List.aggregate([
{$match:
{ productname: { $regex: req.params.query,$options: "i" }}
},
{ $lookup:{
from: "suppliers",
localField: "creator",
foreignField: "unique_SHOP",
as: "source"
}
},
])
You can try $lookup with pipeline,
$match condition of creator id
$project to display required fields
{
$lookup: {
from: "suppliers",
as: "source",
let: { creator: "$creator" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$creator", "$_id"] }
}
},
{
$project: {
_id: 0,
zip: 1,
unique_SHOP: 1
}
}
]
}
}
Playground

Flter mongodb database using mongoose nodejs

I need to filter some users according to some fixed criteria. I have a user collection and a talent collection. The talent collection holds the reference to a master category collection.
What I need is to filter these users according to the category in the talent collection and some keys from the user collection.
For example I need to search for a user whose gender is 'male' and education 'BTech' and will have talents as a programmer and tester
my user collection is like,
{
"_id": "5f1939239bd35429ac9cd78f",
"isOtpVerified": "false",
"role": "user",
"adminApproved": 1,
"status": 0,
"languages": "Malayalam, Tamil, Telugu, Kannada",
"name": "Test user",
"email": "test#email.com",
"phone": "1234567890",
"otp": "480623",
"uid": 100015,
"bio": "Short description from user",
"dob": "1951-09-07T00:00:00.000Z",
"gender": "Male",
"education": "Btech",
"bodyType": "",
"complexion": "",
"height": "",
"weight": "",
"requests": [],
"location": {
"place": "place",
"state": "state",
"country": "country"
},
"image": {
"avatar": "5f1939239bd35429ac9cd78f_avatar.jpeg",
"fullsize": "5f1939239bd35429ac9cd78f_fullsize.png",
"head_shot": "5f1939239bd35429ac9cd78f_head_shot.jpeg",
"left_profile": "5f1939239bd35429ac9cd78f_left_profile.png",
"right_profile": "5f1939239bd35429ac9cd78f_right_profile.png"
},
"__v": 42,
"createdAt": "2020-07-23T07:15:47.387Z",
"updatedAt": "2020-08-18T18:54:22.272Z",
}
Talent collection
[
{
"_id": "5f38efef179aca47a0089667",
"userId": "5f1939239bd35429ac9cd78f",
"level": "5",
"chars": {
"type": "Fresher",
},
"category": "5f19357b50bcf9158c6be572",
"media": [],
"createdAt": "2020-08-16T08:35:59.692Z",
"updatedAt": "2020-08-16T08:35:59.692Z",
"__v": 0
},
{
"_id": "5f3b7e6f7e322948ace30a2c",
"userId": "5f1939239bd35429ac9cd78f",
"level": "3",
"chars": {
"type": "Fresher",
},
"category": "5f19359250bcf9158c6be573",
"media": [
{
"adminApproved": 0,
"status": 0,
"_id": "5f3c22573065f84a48e04a14",
"file": "id=5f1939239bd35429ac9cd78f&dir=test&img=5f1939239bd35429ac9cd78f_image_undefined.jpeg",
"description": "test",
"fileType": "image",
"caption": "test file"
},
{
"adminApproved": 0,
"status": 0,
"_id": "5f3c2d7a8c7f8336b0bfced2",
"file": "id=5f1939239bd35429ac9cd78f&dir=test&img=5f1939239bd35429ac9cd78f_image_1.jpeg",
"description": "this is a demo poster for testing",
"fileType": "image",
"caption": "A Test Poster"
}
],
"createdAt": "2020-08-18T07:08:31.532Z",
"updatedAt": "2020-08-18T19:35:22.899Z",
"__v": 2
}
]
And the category in the above document is a separate one populated to this. the category collection as,
[
{
"_id": "5f19359250bcf9158c6be573",
"status": true,
"title": "Testing",
"description": "Application tester",
"code": "test",
"characteristics": [],
"createdAt": "2020-07-23T07:00:34.221Z",
"updatedAt": "2020-07-23T07:00:34.221Z",
"__v": 0
},
{
"status": true,
"_id": "5f29829a705b4e648c28bc88",
"title": "Designer",
"description": "UI UX Designer",
"code": "uiux",
"createdAt": "2020-08-04T15:45:30.125Z",
"updatedAt": "2020-08-04T15:45:30.125Z",
"__v": 0
},
{
"_id": "5f19357b50bcf9158c6be572",
"status": true,
"title": "programming",
"description": "Java programmer",
"code": "program",
"createdAt": "2020-07-23T07:00:11.137Z",
"updatedAt": "2020-07-23T07:00:11.137Z",
"__v": 0
}
]
So my filter terms will be;
{
categories: ["5f19359250bcf9158c6be573", "5f19357b50bcf9158c6be572"],
minAge: 18,
maxAge: 25,
minHeight: 5,
maxHeight: 6,
minWeight: 50,
maxWeight: 80,
complexion: "white",
gender: "male",
}
And the expected result will be a user have both the above talents and followed conditions,
{
users: { ..User details.. },
medias: { ...medias from the matching talents.. }
}
If there are two collections you need to join them either by primary key or _id with foriegn fields and you can use $lookup with $match to filter down.
Documentation
You need to use $lookup with pipeline,
$match you condition for category match
$lookup to join users collection
$match conditions for users collections fields
$match exclude documents that don't found matching users of criteria passed in conditions
db.talents.aggregate([
{
$match: {
category: { $in: ["5f19359250bcf9158c6be573", "5f19357b50bcf9158c6be572"] }
}
},
{
$lookup: {
from: "users",
as: "users",
let: { userId: "$userId" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$$userId", "$_id"] },
{ $eq: ["$gender", "Male"] },
{ $eq: ["$education", "Btech"] }
// ... add you other match criteria here
]
}
}
}
]
}
},
{ $match: { users: { $ne: [] } } }
])
Playground

Resources