How to lookup subquery documents with another collection moongoose - node.js

I need to join user collections two times as same user will post product and purchased user details as sub document.
Product Schema:
{
product_title: String,
product_desc: String,
Product_purchased:[
{
userid: mongoose.Schema.ObjectId,
purchased_date: Date
}]
posteduserId: mongoose.Schema.ObjectId
}
Users Schema:
{
_id: userId,
name: String,
Pic: String
}
Example document:
[
{
"product_title":"title",
"product_desc":"desc",
"Product_purchased":[
{
"userid":"5d4hvh7duc7c7c8d9scbe",
"name":"name",
"Pic":"url",
"purchased_date":"Date"
},
{
"userid":"5d4hvh7duc7c7c8d9scbe",
"name":"name",
"Pic":"url",
"puchased_date":"Date"
}
],
"posteduserId": "5d4hvh7duc7c7c8d9scbe",
"userid": "5d4hvh7duc7c7c8d9scbe",
"name": "name",
"pic": "url",
}
]
Join same user table with posted userid and subarray of purchased userIds.
Please help me how to crack this one, Thanks in advance.

First you need to fix your Product schema like this to include the ref field:
const mongoose = require("mongoose");
const ProductSchema = new mongoose.Schema({
product_title: String,
product_desc: String,
Product_purchased: [
{
userid: {
type: mongoose.Schema.ObjectId,
ref: "User"
},
purchased_date: Date
}
],
posteduserId: {
type: mongoose.Schema.ObjectId,
ref: "User"
}
});
module.exports = mongoose.model("Product", ProductSchema);
I setup user model like this:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
name: String,
Pic: String
});
module.exports = mongoose.model("User", UserSchema);
The ref User value must match the model name User in the user model.
Then you need to populate two times like this:
router.get("/products", async (req, res) => {
const result = await Product.find({})
.populate("Product_purchased.userid")
.populate("posteduserId");
res.send(result);
});
Let's say you have this 3 users:
{
"_id": "5e133deb71e32b6a68478ab4",
"name": "user1 name",
"Pic": "user1 pic",
"__v": 0
},
{
"_id": "5e133df871e32b6a68478ab5",
"name": "user2 name",
"Pic": "user2 pic",
"__v": 0
},
{
"_id": "5e133e0271e32b6a68478ab6",
"name": "user3 name",
"Pic": "user3 pic",
"__v": 0
}
And this product:
{
"_id": "5e133e9271e32b6a68478ab8",
"product_title": "product1 title",
"product_desc": "product1 description",
"Product_purchased": [
{
"purchased_date": "2020-01-06T14:02:14.029Z",
"_id": "5e133e9271e32b6a68478aba",
"userid": "5e133df871e32b6a68478ab5"
},
{
"purchased_date": "2020-01-06T14:02:14.029Z",
"_id": "5e133e9271e32b6a68478ab9",
"userid": "5e133e0271e32b6a68478ab6"
}
],
"posteduserId": "5e133deb71e32b6a68478ab4",
"__v": 0
}
The result will be:
[
{
"_id": "5e133e9271e32b6a68478ab8",
"product_title": "product1 title",
"product_desc": "product1 description",
"Product_purchased": [
{
"purchased_date": "2020-01-06T14:02:14.029Z",
"_id": "5e133e9271e32b6a68478aba",
"userid": {
"_id": "5e133df871e32b6a68478ab5",
"name": "user2 name",
"Pic": "user2 pic",
"__v": 0
}
},
{
"purchased_date": "2020-01-06T14:02:14.029Z",
"_id": "5e133e9271e32b6a68478ab9",
"userid": {
"_id": "5e133e0271e32b6a68478ab6",
"name": "user3 name",
"Pic": "user3 pic",
"__v": 0
}
}
],
"posteduserId": {
"_id": "5e133deb71e32b6a68478ab4",
"name": "user1 name",
"Pic": "user1 pic",
"__v": 0
},
"__v": 0
}
]

Related

populate data using array from other collection

one collection is carts
[{
"_id": {
"$oid": "633a71cd3a9706174ef57f41"
},
"ProductName": "Natural Blue Sapphire",
"Quantity": 1,
"Price": 20100
},
{
"_id": {
"$oid": "633ae81727e12df99604d9d7"
},
"ProductName": "Natural Blue Sapphire",
"Quantity": 1,
"Price": 20100
}]
`
other collection is orderhistories
`
[{
"_id": {
"$oid": "62f399fc5001311d30680e81"
},
"Address": {
"firstName": "Ronit"
},
"MemberId": {
"$oid": "62e50e29adb262e58ad1f920"
},
"cartItems": [
{
"$oid": "62f399985001311d30680e7e"
}
],
"status": "Delivered"
},{
"_id": {
"$oid": "62ff2b0f0fe010c6e4b0a05d"
},
"Address": {
"firstName": "Ronit"
},
"MemberId": {
"$oid": "62e50e29adb262e58ad1f920"
},
"cartItems": [
{
"$oid": "62f399985001311d30680e7e"
}
],
"status": "Created"
}
`
i want to get cart list using cartItems doc in orderhistories collection
and i am doing this
`
const order = await OrderHistory.find({$or:[{MemberId:Id}] }).sort({"createdAt": -1}).populate('offerCode MemberId cartItems.cart')
res.json(order)
`
my order history model is
`
const orderHistory = new mongoose.Schema({
Address:{
type: mongoose.SchemaTypes.Mixed,
},
MemberId:{
type: mongoose.Schema.Types.ObjectId,
ref:'Member_detail'
},
cartItems:[{
cart:{
type: mongoose.Schema.Types.ObjectId,
ref:'cart'
}
}],
status:{
type:String,
default:"Created"
},
}, {
timestamps: true
})
`
and my cart model schima is
`
const cart_schema = new mongoose.Schema({
Member_Id:{
type: mongoose.Schema.Types.ObjectId,
ref:'Member_detail'
},
ProductId:{
type: mongoose.Schema.Types.ObjectId,
ref:'Product_details'
},
ProductName: {
type: String,
},
status:{type:Number ,
default:1} // 1 = Active 0 = inactive i.e order placed
}, {
timestamps: true
})
`
I need data from carts collection when find ordershistory collection. in orderhistry collection have carts doc. so I need data all data according to id in carts doc
Expected Output
[
{
"_id": "636e73fe398888caca60da16",
"cartItems": [
{
"_id": "6379d76e6274ddb0910f72bc",
"ProductName": "Natural Blue Sapphire",
"Price": 1000,
}
],
"total": 1802.5,
}]

How to query from two collections in mongodb using aggregate?

I have the two following collections in my mongoDB:
PRODUCTS:
[
{
"_id": "5ebb0984e95e3e9e35aab3bf",
"name": "Product 1",
"brand": "ABC",
"code": "7891910000190",
"description": "Lorem Ipsum"
},
{
"_id": "5ebdb9f3d943ae000a4a9714",
"name": "Product 2",
"brand": "DEF",
"code": "7891910000190",
"description": "Lorem Ipsum"
}
]
STOCK GOODS:
[
{
"_id": "5ed0586b7f2cfe02387d0706",
"companyId": "5ed0491bf9a892a5fd9b4d9b",
"branchId": "5ed049a8f9a892a5fd9b4d9e",
"inventoryId": "5ed04d01e9e43cee79734b95",
"productId": "5ebb0984e95e3e9e35aab3bf"
},
{
"_id": "5ed058da75e4e01013f5779d",
"companyId": "5ed0491bf9a892a5fd9b4d9b",
"branchId": "5ed049a8f9a892a5fd9b4d9e",
"inventoryId": "5ed04cede9e43cee79734b93",
"productId": "5ebb0984e95e3e9e35aab3bf"
},
{
"_id": "5ed059483da3bafccb34cec3",
"companyId": "5ed0491bf9a892a5fd9b4d9b",
"branchId": "5ed049a8f9a892a5fd9b4d9e",
"inventoryId": "5ed04d01e9e43cee79734b95",
"productId": "5ebb0984e95e3e9e35aab3bf"
}
]
I want to get the follow result:
[
{
"_id": "5ed0586b7f2cfe02387d0706",
"data": {
"_id": "5ebb0984e95e3e9e35aab3bf",
"brand": "ABC",
"code": "7891910000190",
"description": "Lorem Ipsum",
"name": "Product 1",
}
},
{
"_id": "5ed059483da3bafccb34cec3",
"data": {
"_id": "5ebb0984e95e3e9e35aab3bf",
"brand": "ABC",
"code": "7891910000190",
"description": "Lorem Ipsum",
"name": "Product 1",
}
}
]
My NodeJS and MongoDB aggregate look like this:
const mongoose = require("mongoose");
const StockGoods = mongoose.model("StockGoods");
ObjectId = require("mongodb").ObjectID;
exports.getProducts = async (companyId, branchId, inventoryId) => {
const response = await StockGoods.aggregate([
{
$match: {
companyId: new ObjectId("5ed0491bf9a892a5fd9b4d9b"), // new ObjectId(companyId)
inventoryId: new ObjectId("5ed04d01e9e43cee79734b95"), // new ObjectId(inventoryId)
branchId: new ObjectId("5ed049a8f9a892a5fd9b4d9e"), // new ObjectId(branchId)
},
},
{
$lookup: {
from: "products",
localField: "productId",
foreignField: "_id",
as: "inventory_docs",
},
},
{
$project: {
data: "$inventory_docs",
},
},
{ $unwind: "$data" },
]);
return response;
};
This is the same database and the aggregate function described above, you can check this: https://mongoplayground.net/p/FRgAIfO2bwh.
But, this function aggregate does not working when I use nodejs.
My API returns nothing (empty array).
Whats wrong?
Issue here is the way you have stored data in collection.... StockGoods collection has companyId, inventoryId and branchIdas a string but aggregation is looking for ObjectId aee below match criteria:
$match: {
companyId: new ObjectId("5ed0491bf9a892a5fd9b4d9b"), // new ObjectId(companyId)
inventoryId: new ObjectId("5ed04d01e9e43cee79734b95"), // new
ObjectId(inventoryId)
branchId: new ObjectId("5ed049a8f9a892a5fd9b4d9e"), // new ObjectId(branchId)
},
Based on the data you store in DB either you need to update match with string.
So below Aggregation will work for you..
[
{
"$match": {
"companyId": "5ed0491bf9a892a5fd9b4d9b",
"inventoryId": "5ed04d01e9e43cee79734b95",
"branchId": "5ed049a8f9a892a5fd9b4d9e"
}
},
{
"$lookup": {
"from": "products",
"localField": "productId",
"foreignField": "_id",
"as": "inventory_docs"
}
},
{
"$project": {
"data": "$inventory_docs"
}
},
{
"$unwind": "$data"
}
]
This works you can test in Compass Aggregator tab
[RESOLVED]
The problem was in the scheme:
companyId: {
type: String,
required: true,
},
The correct is:
companyId: {
type: Schema.Types.ObjectId,
required: true,
},

Create Mongoose Find and populate from multiple tables with property set

I'll be grateful for help with Mongoose. I have 3 tables: (Users)Table of users, (Animals)table of animals and table AnimalComments. So (AnimalComments)table reference users and animals.
const schemaComment = new mongoose.Schema({
userRef: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
animalRef: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Animal'
},
content: {
type: String
}
});
const schemaAnimal = new mongoose.Schema({
name: {
type: String
},
isCommentedByMe: {
type : Boolean,
default: false
},
commentCount: {
type : Number,
default: 0
}
});
What I want: I have animals. Users can add comment to animal. When user comment animal his comment is added to table AnimalComments where is stored userRef (userId), animalRef (animalId) and user comment text. Then in request response I want return all animals from table Animals but I need update property commentCount and isCommentedByMe based on value from table AnimalComments.
Response from table Animals:
{
"animals": [
{
"isCommentedByMe": false,
"commentCount": 0,
"name": "Jessica",
"userRef": {
"id": "5dc9bdf3dd5cae00177e184d"
},
"id": "5dcedd48368e9800176f2ef3"
}
]
}
Response from table Users:
{
"users": [
{
"name": "Jony Cash",
"id": "5dc9bdf3dd5cae00177e184d"
}
]
}
Response from table AnimalComments:
{
"comments": [
{
"userRef": "5dc9bdf3dd5cae00177e184d",
"animalRef": "5dcedd48368e9800176f2ef3",
"content": "Sample text"
}
]
}
I want result for exmaple:
{
"animals": [
{
"isCommentedByMe": true,
"commentCount": 4,
"name": "Jessica",
"userRef": {
"id": "5dc9bdf3dd5cae00177e184d"
},
"id": "5dcedd48368e9800176f2ef3"
}
]
}
You don't need to keep isCommentedByMe and commentCount fields in animal schema.
And you need to be able access comments from your animals. But in animal schema there is no field to make that connection. So we need to use virtual population.
So your animal schema must be like this:
const schemaAnimal = new mongoose.Schema(
{
name: {
type: String
}
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
);
// Virtual populate
schemaAnimal.virtual("comments", {
ref: "Comment", //must be changed to the name you used for Comment model.
foreignField: "animalRef",
localField: "_id"
});
Now, we can use the following code to populate the comments.
router.get("/animals", async (req, res) => {
const animals = await Animal.find({}).populate("comments");
res.send(animals);
});
This will give you a result like this:
[
{
"_id": "5dd66c73069f88614c12b394",
"name": "Animal 1",
"__v": 0,
"comments": [
{
"_id": "5dd66cfd069f88614c12b39a",
"userRef": "5dd66b54c5195127ec5a1b82",
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 1 - Animal 1",
"__v": 0
},
{
"_id": "5dd66d30069f88614c12b39d",
"userRef": "5dd66b71c5195127ec5a1b83",
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 2 - Animal 1",
"__v": 0
}
],
"id": "5dd66c73069f88614c12b394"
},
{
"_id": "5dd66c7d069f88614c12b395",
"name": "Animal 2",
"__v": 0,
"comments": [
{
"_id": "5dd66d09069f88614c12b39b",
"userRef": "5dd66b54c5195127ec5a1b82",
"animalRef": "5dd66c7d069f88614c12b395",
"content": "User 1 - Animal 2",
"__v": 0
}
],
"id": "5dd66c7d069f88614c12b395"
},
{
"_id": "5dd66c88069f88614c12b396",
"name": "Animal 3",
"__v": 0,
"comments": [
{
"_id": "5dd66d46069f88614c12b39e",
"userRef": "5dd66b71c5195127ec5a1b83",
"animalRef": "5dd66c88069f88614c12b396",
"content": "User 2 - Animal 3",
"__v": 0
}
],
"id": "5dd66c88069f88614c12b396"
}
]
To convert this result to what you wanted, we can use map like this:
Please note that, you need to set loggedInUserId variable to logged in user's id.
router.get("/animals", async (req, res) => {
const loggedInUserId = "5dd66b54c5195127ec5a1b82";
const animals = await Animal.find({}).populate("comments");
const result = animals.map(animal => {
return {
id: animal._id,
name: animal.name,
isCommentedByMe:
animal.comments.filter(c => c.userRef.toString() === loggedInUserId)
.length > 0,
commentCount: animal.comments.length
};
});
res.send(result);
});
The result will be like this:
[
{
"id": "5dd66c73069f88614c12b394",
"name": "Animal 1",
"isCommentedByMe": true,
"commentCount": 2
},
{
"id": "5dd66c7d069f88614c12b395",
"name": "Animal 2",
"isCommentedByMe": true,
"commentCount": 1
},
{
"id": "5dd66c88069f88614c12b396",
"name": "Animal 3",
"isCommentedByMe": false,
"commentCount": 1
}
]
And the answer for the question in the comments is: (how to reference User)
const animals = await Animal.find({}).populate({
path: "comments",
model: Comment,
populate: [
{
path: "userRef",
model: User
}
]
});
This will give you the userRef like this:
[
{
"_id": "5dd66c73069f88614c12b394",
"name": "Animal 1",
"__v": 0,
"comments": [
{
"_id": "5dd66cfd069f88614c12b39a",
"userRef": {
"_id": "5dd66b54c5195127ec5a1b82",
"name": "User 1",
"__v": 0
},
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 1 - Animal 1",
"__v": 0
},
{
"_id": "5dd66d30069f88614c12b39d",
"userRef": {
"_id": "5dd66b71c5195127ec5a1b83",
"name": "User 2",
"__v": 0
},
"animalRef": "5dd66c73069f88614c12b394",
"content": "User 2 - Animal 1",
"__v": 0
}
],
"id": "5dd66c73069f88614c12b394"
},
{
"_id": "5dd66c7d069f88614c12b395",
"name": "Animal 2",
"__v": 0,
"comments": [
{
"_id": "5dd66d09069f88614c12b39b",
"userRef": {
"_id": "5dd66b54c5195127ec5a1b82",
"name": "User 1",
"__v": 0
},
"animalRef": "5dd66c7d069f88614c12b395",
"content": "User 1 - Animal 2",
"__v": 0
}
],
"id": "5dd66c7d069f88614c12b395"
},
{
"_id": "5dd66c88069f88614c12b396",
"name": "Animal 3",
"__v": 0,
"comments": [
{
"_id": "5dd66d46069f88614c12b39e",
"userRef": {
"_id": "5dd66b71c5195127ec5a1b83",
"name": "User 2",
"__v": 0
},
"animalRef": "5dd66c88069f88614c12b396",
"content": "User 2 - Animal 3",
"__v": 0
}
],
"id": "5dd66c88069f88614c12b396"
}
]

how to filter after populated mongodb [duplicate]

This question already has answers here:
Querying after populate in Mongoose
(6 answers)
Closed 5 years ago.
I need to filter my business document according to a type of service.
var BusinessSchema = Schema({
name: String,
slug: String,
service: [{type: Schema.ObjectId, ref: 'Service'}],
owner: {type: Schema.ObjectId, ref: 'User'}
});
module.exports = mongoose.model('Business', BusinessSchema);
And Schema service is:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ServiceSchema = Schema({
name: String,
});
module.exports = mongoose.model('Service', ServiceSchema);
Of this array of businesses would only need those that have within its services the "Service A" for example.
"business": [
{
"_id": "594957df4e195d2500324726",
"owner": "59482e80d4df7208503154b8",
"slug": "Almacene2s",
"name": "Almacene2s",
"__v": 0,
"service": [
{
"_id": "594ab160778ae82c44af3a78",
"name": "Service C",
"__v": 0
},
{
"_id": "594ab1be778ae82c44af3a7a",
"name": "Service D",
"__v": 0
}
],
},
{
"_id": "5948483e6bcc1f2788b09145",
"owner": "59482e80d4df7208503154b8",
"slug": "guaranda-2",
"name": "Guaranda Fig",
"service": [
{
"_id": "594ab160778ae82c44af3a78",
"name": "Service A",
"__v": 0
},
{
"_id": "594ab1be778ae82c44af3a7a",
"name": "Service B",
"__v": 0
}
],
}
]
In this case the result I want to get is:
{
"_id": "5948483e6bcc1f2788b09145",
"owner": "59482e80d4df7208503154b8",
"slug": "guaranda-2",
"name": "Guaranda Fig",
"service": [
{
"_id": "594ab160778ae82c44af3a78",
"name": "Service A",
"__v": 0
},
{
"_id": "594ab1be778ae82c44af3a7a",
"name": "Service B",
"__v": 0
}
],
}
It is very important for me to be able to carry out this search, because the system will allow the user to choose as a filter the business that has the desired service.
It's my first project on nodejs + mongodb and I would like your help before continuing on. Thank you very much
Using $lookup, here's what you can achieve:
Service.aggregate([
{
$match: {
"name": "Service A"
}
},
{
$lookup: {
from: "business",
localField: "_id",
foreignField: "service",
as: "businesses"
}
}
], function(err, result) {
console.log(result);
});
In this aggregation pipeline, you first match and find service documents with name "Service A". Then lookup in service array of business documents for matching ones and place them in businesses field. The result will look like this:
{
"_id" : "594af96883ab76db8ecd021b",
"name" : "Service A",
"businesses" : [
{
"_id" : "594af9a683ab76db8ecd0225",
"slug" : "Almacene2s",
"owner" : "59482e80d4df7208503154b8",
"name" : "Almacene2s",
"service" : [
"594af96883ab76db8ecd021b",
"594af96883ab76db8ecd021d"
]
}
]
}

Returning all fields from MonoDB matched by value of a field embedded in Object type

for the following MongoDB results, i'm trying to write a query where the name filed is NOT Demo or Demo 2 the Items.location is equal to hongkong:
{
"_id": ObjectID("573ac4d1ad364cd534a03e15"),
"updatedAt": ISODate("2016-05-17T07:14:25.341Z"),
"createdAt": ISODate("2016-05-17T07:14:25.341Z"),
"name": "Testing",
"Items": {
"date": "18052016",
"location": "hongkong"
},
"__v": 0
},
{
"_id": ObjectID("573ac4d1ad364cd534a03e16"),
"updatedAt": ISODate("2016-05-17T07:14:25.341Z"),
"createdAt": ISODate("2016-05-17T07:14:25.341Z"),
"name": "Demo",
"Items": {
"date": "18052016",
"location": "hongkong demo"
},
"__v": 0
},
{
"_id": ObjectID("573ac4d1ad364cd534a03e16"),
"updatedAt": ISODate("2016-05-17T07:14:25.341Z"),
"createdAt": ISODate("2016-05-17T07:14:25.341Z"),
"name": "Demo 2",
"Items": {
"date": "18052016",
"location": "hongkong demo"
},
"__v": 0
}
My query looks like:
mySchema.statics.testing = function *() {
var output = this.find(
{
name: {
$nin: ["Demo", "Demo 2"]
}
}
,{
Items: {
$elemMatch: { location: "hongkong" }
}
}).exec();
return output;
};
and my schema is below:
var mongoose = require('mongoose');
var mySchema = new mongoose.Schema({
name: String,
Items: Object
}, { timestamps: true });
The query seems to only return the "_id" field for the matched where as i want the all the fields returned, name, Items..etc. What am i missing?
try this code
mySchema.statics.testing = function (fn) {
this.find(
{
name: {
$nin: ["Demo", "Demo 2"]
},
"Items.location": "hongkong"
})
.exec(function (err, docs) {
fn(err, docs)
})
};
fixed. Needed:
var output = this.find(
{
name: {
$nin: ["Demo", "Demo 2"]
},
"Items.location" : "hongkong"
}).exec();
return output;
};

Resources