Related
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,
},
When i combine 2 table to fetch data from mongoDB collection struck with my expected out. please any one help me to fix the same pleas.
Collection:
recipecatagories
{
"_id":{"$oid":"5dada3c5761bb32a1201d4da"},
"CategoryName":"Biryani"
}
{
"_id":{"$oid":"5dada3c5761bb32a1201d4db"},
"CategoryName":"Mutton Biryani"
}
{
"_id":{"$oid":"5dada3c5761bb32a1201d4d4"},
"CategoryName":"Chicken Biryani"
}
{
"_id":{"$oid":"5daea43a517cf601a7e80a3b"},
"CategoryName":"Kathirikai gothsu"
}
recipes:
{
"_id":{"$oid":"5daffda85d9b4fd19ae4da30"},
"recipeTitle":"Mutton dum biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[{"$oid":"5dada3c5761bb32a1201d4da"},{"$oid":"5dada3c5761bb32a1201d4db"},{"$oid":"5dada3c5761bb32a1201d4dc"}],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn":{"$date":{"$numberLong":"0"}},
"postedBy":"shiva#yopmail.com"
}
{
"_id":{"$oid":"5daffda85d9b4fd19ae4da30"},
"recipeTitle":"Mutton Chicken biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[{"$oid":"5dada3c5761bb32a1201d4da"},{"$oid":"5dada3c5761bb32a1201d4d4"},{"$oid":"5dada3c5761bb32a1201d4dc"}],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn":{"$date":{"$numberLong":"0"}},
"postedBy":"shiva#yopmail.com"
}
users:
{
"_id":{"$oid":"5da428b85e3cbd0f153c7f3b"},
"emailId":"shiva#yopmail.com",
"fullName":"siva prakash",
"accessToken":"xxxxxxxxxxxxx",
"__v":{"$numberInt":"0"}
}
Current mongoose code in node js
RecipeCatagory.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(id) } },
{
"$lookup": {
"from": "recipes",
"localField": "_id",
"foreignField": "recipeCategoryId",
"as": "recipes"
}
},
{ "$unwind": "$recipes" },
{ "$unwind": "$recipes.recipeCategoryId" },
{
"$match": {
"recipes.recipeCategoryId": mongoose.Types.ObjectId(id)
}
},
{
"$lookup": {
"from": "users",
"localField": "emailId",
"foreignField": "recipes.postedBy",
"as": "users"
}
},
])
.exec(function (err, recipes) {
if (err) {
response
.status(400)
.json({
"status": "Failed",
"message": "Error",
"data": err | err.message
});
return
} else {
response
.status(200)
.json({
"status": "Ok",
"message": "Success",
"data": recipes
});
return
}
})
Current Output using above Query
{
"status": "Ok",
"message": "Success",
"data": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"recipes": {
"_id": "5daffda85d9b4fd19ae4da30",
"recipeTitle": "Mutton dum biryani",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": false,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "42eb19a0-ee57-11e9-86f7-a7b758fb7271",
"__v": 0
}
]
},
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"recipes": {
"_id": "5daffda85d9b4fd19ae4da31",
"recipeTitle": "Kumbakonam kathirikai gothsu",
"Recipetags": [
"Indian",
"Veg"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": true,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxxx",
"__v": 0
}
]
}
]
}
**Expected Out:**
{
"status": "Ok",
"message": "Success",
"data": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "chiken Biryani",
"recipes": {
"_id": "5daffda85d9b4fd19ae4da30",
"recipeTitle": "Mutton dum biryani",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": false,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "42eb19a0-ee57-11e9-86f7-a7b758fb7271",
"__v": 0
}
]
},
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"recipes": [
{
"_id": "5daffda85d9b4fd19ae4da31",
"recipeTitle": "Mutton dum biryani",
"Recipetags": [
"Indian",
"Veg"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": true,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
{
"_id": "5daffda85d9b4fd19ae4da31",
"recipeTitle": "Chicken biryani",
"Recipetags": [
"Indian",
"Veg"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": true,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
}
],
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxx",
"__v": 0
}
]
}
]
}
i am struck to get expected out put... i want recipes as array which has recipecategory has in recipe collection...
You are basically doing this the wrong way around and should instead be querying from the Recipe model. You do already have the "category id values" which are contained within an array of that document.
Basically you should have something like this:
const wantedCategories = [
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4db")
];
let data = await Recipe.aggregate([
// Match wanted category(ies)
{ "$match": {
"recipeCategoryId": { "$in": wantedCategories }
}},
// Filter the content of the array
{ "$addFields": {
"recipeCategoryId": {
"$filter": {
"input": "$recipeCategoryId",
"cond": {
"$in": [ "$$this", wantedCategories ]
}
}
}
}},
// Lookup the related matching category(ies)
{ "$lookup": {
"from": RecipeCategory.collection.name,
"let": { "recipeCategoryIds": "$recipeCategoryId" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$recipeCategoryIds" ] }
}}
],
"as": "recipeCategoryId"
}},
// Lookup the related user to postedBy
{ "$lookup": {
"from": User.collection.name,
"let": { "postedBy": "$postedBy" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$emailId", "$$postedBy" ] } } }
],
"as": "postedBy"
}},
// postedBy is "singular"
{ "$unwind": "$postedBy" }
]);
Which would return a result like this:
{
"data": [
{
"_id": "5dbce992010163139853168c",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"recipeCategoryId": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"__v": 0
},
{
"_id": "5dada3c5761bb32a1201d4db",
"CategoryName": "Mutton Biryani",
"__v": 0
}
],
"recipeTitle": "Mutton dum biryani",
"cookTime": "30 Mins",
"recepeType": false,
"postedBy": {
"_id": "5dbce992010163139853168e",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxxx",
"__v": 0
},
"__v": 0
},
{
"_id": "5dbce992010163139853168d",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"recipeCategoryId": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"__v": 0
}
],
"recipeTitle": "Mutton Chicken biryani",
"cookTime": "30 Mins",
"recepeType": false,
"postedBy": {
"_id": "5dbce992010163139853168e",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxxx",
"__v": 0
},
"__v": 0
}
]
}
Note: I do actually correct the english spelling of a model with RecipeCategory instead of RecipeCatagory as shown in the question. Apply to your own implementation as you wish.
You might note the usage of $in with a "list of ids" in both the query form and the aggregation operator form at different stages. Personally I would code this in this way even if there was only a single value supplied at the present time, since it means there would be little to change other than the input variable to the method in the event I wanted multiple values, such as "multiple categories" within a faceted search option for example.
So this demonstrates the "list" argument approach, though it still applies to singular values as in the question.
The whole process follows what the comments say on each pipeline stage, being that you first match wanted "documents" from the recipes collection by the selected "category" value(s). Then we just want to remove any non-matching data for the category within the array of those documents. This could actually be viewed as "optional" if you wanted to just show ALL categories associated with that recipe whether they matched the criteria or not. Where this is the case, all you need do is remove the stage containing the $filter statement, and the code will happily work in that way.
Then of course there are the $lookup stages, being one for each related collection. The example here actually shows the expressive form of the $lookup pipeline stage. This again is really only for demonstration as the standard localField and foreignField form is perfectly fine for the purposes of what you want to do here, and the further syntax is not needed. MongoDB will basically transform that older syntax into the newer expressive form as shown internally anyway.
You might note the usage of Model.collection.name in the from argument though. This is actually a handy thing to do when coding with mongoose. MongoDB itself expects the actual collection name as the argument here. Since mongoose will typically pluralize the model name provided for the actual collection referenced, or otherwise takes an explicit argument to the model definition, then using the .collection.name accessor from the model ensures you have the correct actual collection name, even if this changes at some time within the model definition.
The only other simple step here is the $unwind at the end, and only because the output of $lookup is always an array, and here the replacement of the postedBy property with matched related content is always expected to be only one item. So for simple readability of results, we can just make this a single value instead of having an array here.
For a bit more context into how that all comes together, here is the code for the statement and the creation of the data all in a self contained listing, from which of course the "output" posted above was actually obtained:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/menu';
const options = { useNewUrlParser: true, useUnifiedTopology: true };
mongoose.set('debug', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
const recipeCategorySchema = new Schema({
CategoryName: String
});
const recipeSchema = new Schema({
recipeTitle: String,
Recipetags: [String],
cookTime: String,
recipeCategoryId: [{ type: Schema.Types.ObjectId, ref: 'RecipeCategory' }],
recipeCuisineId: String,
recepeType: Boolean,
availableStreaming: String,
postedBy: String
});
const userSchema = new Schema({
emailId: String,
fullName: String,
accessToken: String
});
const RecipeCategory = mongoose.model('RecipeCategory', recipeCategorySchema);
const Recipe = mongoose.model('Recipe', recipeSchema);
const User = mongoose.model('User', userSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, options);
// Clean data for demonstration
await Promise.all(
Object.values(conn.models).map(m => m.deleteMany())
);
// Insert some data
await RecipeCategory.insertMany([
{
"_id": ObjectId( "5dada3c5761bb32a1201d4da"),
"CategoryName":"Biryani"
},
{
"_id": ObjectId("5dada3c5761bb32a1201d4db"),
"CategoryName":"Mutton Biryani"
},
{
"_id": ObjectId("5dada3c5761bb32a1201d4d4"),
"CategoryName":"Chicken Biryani"
},
{
"_id": ObjectId("5daea43a517cf601a7e80a3b"),
"CategoryName":"Kathirikai gothsu"
}
]);
await Recipe.insertMany([
{
"recipeTitle":"Mutton dum biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4db"),
ObjectId("5dada3c5761bb32a1201d4dc")
],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn": new Date(),
"postedBy":"shiva#yopmail.com"
},
{
"recipeTitle":"Mutton Chicken biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4d4"),
ObjectId("5dada3c5761bb32a1201d4dc")
],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn": new Date(),
"postedBy":"shiva#yopmail.com"
}
]);
await User.create({
"emailId":"shiva#yopmail.com",
"fullName":"siva prakash",
"accessToken":"xxxxxxxxxxxxx",
});
const wantedCategories = [
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4db")
];
let data = await Recipe.aggregate([
// Match wanted category(ies)
{ "$match": {
"recipeCategoryId": { "$in": wantedCategories }
}},
// Filter the content of the array
{ "$addFields": {
"recipeCategoryId": {
"$filter": {
"input": "$recipeCategoryId",
"cond": {
"$in": [ "$$this", wantedCategories ]
}
}
}
}},
// Lookup the related matching category(ies)
{ "$lookup": {
"from": RecipeCategory.collection.name,
"let": { "recipeCategoryIds": "$recipeCategoryId" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$recipeCategoryIds" ] }
}}
],
"as": "recipeCategoryId"
}},
// Lookup the related user to postedBy
{ "$lookup": {
"from": User.collection.name,
"let": { "postedBy": "$postedBy" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$emailId", "$$postedBy" ] } } }
],
"as": "postedBy"
}},
// postedBy is "singular"
{ "$unwind": "$postedBy" }
]);
log({ data });
} catch (e) {
console.error(e)
} finally {
mongoose.disconnect();
}
})()
I need some help with Mongo, Mongoose and Node.js.
In the code below, I'd like to join carrinho and produtos collection to retrieve produtos _id, price and description in the same array/object.
My Carrinho Schema
const Carrinho = new mongoose.Schema(
{
title: {
type: String,
},
produtos: [{
price: Number,
produto: { type: mongoose.Schema.Types.ObjectId, ref:
"Produtos" }
}
],
total: {
type: Number,
},
},
{
timestamps: true
})
My Produtos Schema
const Produtos = new mongoose.Schema(
{
description: {
type: String,
required: true,
},
gtin: {
type: String,
required: true,
unique: true,
},
thumbnail: {
type: String,
},
price: {
type: Number,
}
},
{
timestamps: true
}
)
After reading aggregate documentation this is the best I've got:
Carrinho.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } },
{
"$lookup": {
"from": "produtos",
"localField": "produtos._id",
"foreignField": "_id",
"as": "produtosnocarrinho"
}
},
{
"$addFields": {
"total": {
"$reduce": {
"input": "$produtos",
"initialValue": 0,
"in": { "$add": ["$$value", "$$this.price"] }
}
}
}
}
]).exec((err, data) => {
if (err) res.json(err)
res.json(data)
});
And this is the result:
[
{
"_id": "5cb76d7d99c3f4062f512537",
"title": "Carrinho do Lucas",
"produtos": [
{
"_id": "5cafead2bc648978100d7698",
"price": 20.1
},
{
"_id": "5cae911adf75ac4d3ca4bcb6",
"price": 20.1
},
{
"_id": "5cb0f0adc5fb29105d271499",
"price": 20.1
}
],
"createdAt": "2019-04-17T18:16:29.833Z",
"updatedAt": "2019-04-19T00:50:43.316Z",
"__v": 3,
"produtosnocarrinho": [
{
"_id": "5cae911adf75ac4d3ca4bcb6",
"description": "AÇÚCAR REFINADO UNIÃO 1KGS",
"gtin": "7891910000197",
"thumbnail": "7891910000197",
"createdAt": "2019-04-11T00:58:02.296Z",
"updatedAt": "2019-04-11T00:58:02.296Z",
"__v": 0
},
{
"_id": "5cafead2bc648978100d7698",
"description": "HASBRO MR. POTATO HEAD MALETA DE PEÇAS",
"gtin": "5010994598815",
"thumbnail": "pecas_300x300-PU3435f_1.jpg",
"createdAt": "2019-04-12T01:33:06.628Z",
"updatedAt": "2019-04-12T01:33:06.628Z",
"__v": 0
},
{
"_id": "5cb0f0adc5fb29105d271499",
"description": "REPELENTE EXPOSIS INFANTIL SPRAY",
"gtin": "7898392800055",
"thumbnail": "PU28bb9_1.jpg",
"createdAt": "2019-04-12T20:10:21.363Z",
"updatedAt": "2019-04-12T20:10:21.363Z",
"__v": 0
}
],
"total": 60.300000000000004
}
]
The following Query will be help:
models.Carrinho.aggregate(
[
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } },
{
"$lookup": {
"from": "produtos",
"localField": "produtos._id",
"foreignField": "_id",
"as": "produtosnocarrinho"
}
},
{
"$addFields": {
"total": {
"$reduce": {
"input": "$produtos",
"initialValue": 0,
"in": { "$add": ["$$value", "$$this.price"] }
}
}
}
},
{$unwind : '$produtos'},
{$unwind : '$produtosnocarrinho'},
{$redact: { $cond: [{
$eq: [
"$produtos._id",
"$produtosnocarrinho._id"
]
},
"$$KEEP",
"$$PRUNE"
]
}
},
{ $project: {
_id : 1,
title : 1,
produtosData : {
_id : "$produtos._id",
price : "$produtos.price",
description : "$produtosnocarrinho.description"
},
total : 1,
createdAt: 1,
updatedAt : 1
}
},
{
$group : {
_id : {
_id : '$_id',
title : '$title',
total : '$total',
createdAt : '$createdAt',
updatedAt : '$updatedAt'
},
produtosData: {$push: "$produtosData" }
}
},
{ $project: {
_id : '$_id._id',
title : '$_id.title',
total : '$_id.total',
createdAt : '$_id.createdAt',
updatedAt : '$_id.updatedAt',
produtosData: '$produtosData'
}
}
]).exec((err, data) => {
if (err) res.json(err)
res.json(data)
});
Output :
[{
"_id": "5cbc42c24502a7318952d7b2",
"title": "Carrinho do Lucas",
"total": 60.300000000000004,
"createdAt": "2019-04-21T10:15:30.629Z",
"updatedAt": "2019-04-21T10:15:30.629Z",
"produtosData": [{
"_id": "5cafead2bc648978100d7698",
"price": 20.1,
"description": "HASBRO MR. POTATO HEAD MALETA DE PEÇAS"
}, {
"_id": "5cae911adf75ac4d3ca4bcb6",
"price": 20.1,
"description": "AÇÚCAR REFINADO UNIÃO 1KGS"
}, {
"_id": "5cb0f0adc5fb29105d271499",
"price": 20.1,
"description": "REPELENTE EXPOSIS INFANTIL SPRAY"
}]
}]
performance depends on produtos matching data from Lookup Query As we are doing double Unwind.
I have the following document
{
"userid": "5a88389c9108bf1c48a1a6a7",
"email": "abc#gmail.com",
"lastName": "abc",
"firstName": "xyz",
"__v": 0,
"friends": [{
"userid": "5a88398b9108bf1c48a1a6a9",
"ftype": "SR",
"status": "ACCEPT",
"_id": ObjectId("5a9585b401ef0033cc8850c7")
},
{
"userid": "5a88398b9108bf1c48a1a6a91111",
"ftype": "SR",
"status": "ACCEPT",
"_id": ObjectId("5a9585b401ef0033cc8850c71111")
},
{
"userid": "5a8ae0a20df6c13dd81256e0",
"ftype": "SR",
"status": "pending",
"_id": ObjectId("5a9641fbbc9ef809b0f7cb4e")
}]
},
{
"userid": "5a88398b9108bf1c48a1a6a9",
"friends": [{ }],
"lastName": "123",
"firstName": "xyz",
.......
},
{
"userid": "5a88398b9108bf1c48a1a6a91111",
"friends": [{ }],
"lastName": "456",
"firstName": "xyz",
...
}
First Query
Here I want to get userId from friends array ,which having status equals to "ACCEPT".
ie
[5a88398b9108bf1c48a1a6a9,5a88398b9108bf1c48a1a6a91111]
Second Query
After that, I have to make another query on the same collection to get details of each userid returned in the first query.
final Query will return details of [5a88398b9108bf1c48a1a6a9,5a88398b9108bf1c48a1a6a91111]
both userid ie
[
{
userid" : "5a88398b9108bf1c48a1a6a9",
"lastName" : "123",
"firstName" : "xyz"
},
{
"userid" : "5a88398b9108bf1c48a1a6a91111",
"lastName" : "456",
"firstName" : "xyz"
}
]
I have tried so far with
Users.find ({'_id':5a88389c9108bf1c48a1a6a7,"friends.status":'ACCEPT'}, (error, users) => {})
or
Users.find ({'_id':5a88389c9108bf1c48a1a6a7, friends: { $elemMatch: { status: 'ACCEPT' } } }, (error, users) => {})
Use the aggregation framework's $map and $filter operators to handle the task. $filter will filter the friends array based on the specified condition that the status should equal "ACCESS" and $map will transform the results from the filtered array to the desired format.
For the second query, append a $lookup pipeline step which does a self-join on the users collection to retrieve the documents which match the ids from the previous pipeline.
Running the following aggregate operation will produce the desired array:
User.aggregate([
{ "$match": { "friends.status": "ACCEPT" } },
{ "$project": {
"users": {
"$map": {
"input": {
"$filter": {
"input": "$friends",
"as": "el",
"cond": { "$eq": ["$$el.status", "ACCEPT"] }
}
},
"as": "item",
"in": "$$item.userid"
}
}
} },
{ "$lookup": {
"from": "users",
"as": "users",
"localField": "users",
"foreignField": "userid"
} },
]).exec((err, results) => {
if (err) throw err;
console.log(results[0].users);
});
I did not test it. just for an idea, give it a try and let me know.
db.Users.aggregate(
[
{
$unwind: "$friends"
},
{
$match:{ "$friends.status": "ACCEPT"}
},
{
$project:{ "FriendUserID":"$friends.userid"}
},
{
$lookup:{
from:"Users",
as: "FriendsUsers",
localField: "FriendUserID",
foreignField: "userid"
}
},
{
$project: { FriendsUsers.lastName:1,FriendsUsers.firstName:1 }
}
]
)
filtering nested elements
const products = await Product.aggregate<ProductDoc>([
{
$match: {
userId: data.id,
},
},
{
$project: {
promotions: {
$filter: {
input: '$promotions',
as: 'p',
cond: {
$eq: ['$$p.status', PromotionStatus.Started],
},
},
},
userId: 1,
name: 1,
thumbnail: 1,
},
},
]);
for multiple condition
cond: {
$and: [
{
$eq: [
"$$c.product",
"37sd87hjsdj3"
]
},
{
$eq: [
"$$c.date",
"date-jan-4-2022"
],
}
]
},
I have a simple two collections like below :
assignments:
[
{
"_id": "593eff62630a1c35781fa325",
"topic_id": 301,
"user_id": "59385ef6d2d80c00d9bdef97"
},
{
"_id": "593eff62630a1c35781fa326",
"topic_id": 301,
"user_id": "59385ef6d2d80c00d9bdef97"
}
]
and users collection:
[
{
"_id": "59385ef6d2d80c00d9bdef97",
"name": "XX"
},
{
"_id": "59385b547e8918009444a3ac",
"name": "YY"
}
]
and my intent is, an aggregate query by user_id on assignment collection, and also I would like to include user.name in that group collection. I tried below:
Assignment.aggregate([{
$match: {
"topic_id": "301"
}
},
{
$group: {
_id: "$user_id",
count: {
$sum: 1
}
}
},
{
$lookup: {
"from": "kullanicilar",
"localField": "user_id",
"foreignField": "_id",
"as": "user"
}
},
{
$project: {
"user": "$user",
"count": "$count",
"_id": "$_id"
}
},
But the problem is that user array is always blank.
[ { _id: '59385ef6d2d80c00d9bdef97', count: 1000, user: [] } ]
I want something like :
[ { _id: '59385ef6d2d80c00d9bdef97', count: 1000, user: [_id:"59385ef6d2d80c00d9bdef97",name:"XX"] } ]