Related
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,
}]
I have two mongoose schema one for keywords :
const keywordSchema = new Schema<keywordI>(
{
sentence: { type: String, required: true },
example: { type: String, required: true },
translate: { type: String, required: true },
imageUrl: { type: String, required: true },
audioUrl: { type: String, required: true },
deletedAt: { type: Date, select: false },
},
{ timestamps: true }
);
and one for keywordsUser :
const keywordUserSchema = new Schema<keywordUserI>(
{
keywordId: {
type: mongoose.Types.ObjectId,
ref: "Keyword",
required: true,
},
userId: {
type: mongoose.Types.ObjectId,
ref: "User",
required: true,
},
isBookMarked: { type: Boolean, default: false },
correctAnswer: { type: Number, default: 0 },
wrongAnswer: { type: Number, default: 0 },
},
{ timestamps: true }
);
I want to get keywords by user grouped by type which I calculate from wrongAnswer and correctAnswer
I try to do this using aggregate
return KeyWordUser.aggregate([
{
$match: { userId },
},
{
$addFields: {
type: {
$function: {
body: getType,
args: ["$correctAnswer", "$wrongAnswer"],
lang: "js",
},
},
},
},
{
$group: {
_id: "$type",
words: {
$push: "$keywordId",
},
isBookMarked: {
$push: { isBookMarked: "$isBookMarked", keywordId: "$keywordId" },
},
},
},
{
$lookup: {
localField: "words",
from: "keywords",
foreignField: "_id",
as: "props",
},
},
{
$addFields: { type: "$_id" },
},
{
$project: {
_id: 0,
words: 0,
},
},
]);
I have this result from aggregate
returned data from. postman
I want to add new aggregate stage to get this result
previous result
[
{
"isBookMarked": [
{
"keywordId": "62e2a0ad3113b8234511cd72"
},
{
"isBookMarked": false,
"keywordId": "62e2a0ba3113b8234511cd74"
}
],
"props": [
{
"_id": "62e2a0ad3113b8234511cd72",
"sentence": "hello",
"example": "Hello , what's your name",
"translate": "مرحبا ما اسمك",
"imageUrl": "src/img/keywords/keyword-A7H_M-1659019437698.png",
"audioUrl": "src/audio/keywords/keyword-YyhUp-1659019437700.mpeg",
"createdAt": "2022-07-28T14:43:57.708Z",
"updatedAt": "2022-07-28T14:43:57.708Z",
"__v": 0
},
{
"_id": "62e2a0ba3113b8234511cd74",
"sentence": "hello 2 ",
"example": "Hello , what's your name 2 ",
"translate": "مرحبا ما اسمك 2",
"imageUrl": "src/img/keywords/keyword-2JO7D-1659019450396.png",
"audioUrl": "src/audio/keywords/keyword-kt5Tc-1659019450397.mpeg",
"createdAt": "2022-07-28T14:44:10.400Z",
"updatedAt": "2022-07-28T14:44:10.400Z",
"__v": 0
}
],
"type": "strong"
}
]
and i want this result :
[
{
"props": [
{
"_id": "62e2a0ad3113b8234511cd72",
"sentence": "hello",
"example": "Hello , what's your name",
"translate": "مرحبا ما اسمك",
"imageUrl": "src/img/keywords/keyword-A7H_M-1659019437698.png",
"audioUrl": "src/audio/keywords/keyword-YyhUp-1659019437700.mpeg",
"createdAt": "2022-07-28T14:43:57.708Z",
"updatedAt": "2022-07-28T14:43:57.708Z",
"__v": 0
},
{
"_id": "62e2a0ba3113b8234511cd74",
"isBookMarked" : false
"sentence": "hello 2 ",
"example": "Hello , what's your name 2 ",
"translate": "مرحبا ما اسمك 2",
"imageUrl": "src/img/keywords/keyword-2JO7D-1659019450396.png",
"audioUrl": "src/audio/keywords/keyword-kt5Tc-1659019450397.mpeg",
"createdAt": "2022-07-28T14:44:10.400Z",
"updatedAt": "2022-07-28T14:44:10.400Z",
"__v": 0
}
],
"type": "strong"
}
]
You can just populate with the keywordId not with aggregate
I have two fields for a vote poll like vote1 and vote2 which hold a candidateId. What I was trying to do is, calculate the number of vote counts for each candidate in my VotePoll collection.
My VotePollSchema looks like the following:
VotePollSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
vote1: {
desc: "First Voted Candidate",
type: mongoose.Schema.Types.ObjectId,
ref: "Candidate",
type: String,
},
vote2: {
desc: "Second Voted Candidate",
type: mongoose.Schema.Types.ObjectId,
ref: "Candidate",
type: String,
},
isActive: {
desc: "is Active.",
type: Boolean,
default: true,
required: true,
},
});
What I want to achieve here is that count the number of votes for a candidate which might be in vote1 or vote2. Since each User is able to vote for one or two candidates at the same time.
So, how should I group this collection by the candidate? Which could be either in vote1 or in vote2. I've tried something like this but I don't think it's the appropriate way of doing it.
const votePolls = await VotePoll.aggregate([
{ $match: { isActive: true } },
{
$group: {
_id: {
$or: [{ firstVotedCandidate: "$firstVotedCandidate" }],
},
voteCount: { $sum: 1 },
},
},
]);
My expected result:
{
candidateOne: {
candidateName: "Joshua Solomon"
voteCount: 140
}
candidateTwo: {
candidateName: "Jenny Doe"
voteCount: 25
}
...
}
Sample input data:
[
{
"isActive": true,
"user": {
"fullName": "Joshua Solomon",
"createdAt": "2021-08-02T13:33:14.584Z",
"updatedAt": "2021-08-02T13:36:35.041Z",
"registeredBy": "61026c5e4fd8a53c98b8c579",
"id": "6107f4182e21a42bd8b2b9cb"
},
"vote1": {
"isActive": true,
"fullName": "Candidate 1",
"title": "Available Candidate",
"createdAt": "2021-07-28T12:35:27.868Z",
"updatedAt": "2021-07-28T12:35:27.868Z",
"id": "61014f0f8d0ad041c0cf493a"
},
"vote2": {
"isActive": true,
"fullName": "Candidate 4",
"title": "Fourth Candidate",
"createdAt": "2021-07-28T12:36:13.229Z",
"updatedAt": "2021-07-28T12:36:13.229Z",
"id": "61014f3d8d0ad041c0cf4940"
},
"createdAt": "2021-08-02T13:36:34.859Z",
"updatedAt": "2021-08-02T13:36:34.859Z",
"id": "6107f4e22e21a42bd8b2dae2"
},
{
"isActive": true,
"user": {
"fullName": "Jenny Doe",
"createdAt": "2021-08-02T13:33:15.351Z",
"updatedAt": "2021-08-03T06:10:53.632Z",
"registeredBy": "61026c5e4fd8a53c98b8c579",
"id": "6107f4192e21a42bd8b2d136"
},
"vote1": {
"isActive": true,
"fullName": "Candidate 4",
"title": "Fourth Candidate",
"createdAt": "2021-07-28T12:36:13.229Z",
"updatedAt": "2021-07-28T12:36:13.229Z",
"id": "61014f3d8d0ad041c0cf4940"
},
"vote2": {
"isActive": true,
"fullName": "Candidate 2",
"title": "Second Candidate",
"createdAt": "2021-07-28T12:35:56.425Z",
"updatedAt": "2021-07-28T12:35:56.425Z",
"id": "61014f2c8d0ad041c0cf493c"
},
"createdAt": "2021-08-03T06:10:53.389Z",
"updatedAt": "2021-08-03T06:10:53.389Z",
"id": "6108ddedcc1c4f2a505b4028"
}
]
This aggregate will return results in a sorted array:
[
{
'$addFields': {
'votes': [
'$vote1', '$vote2'
]
}
}, {
'$unwind': {
'path': '$votes'
}
}, {
'$group': {
'_id': {
'name': '$votes.fullName'
},
'voteCount': {
'$sum': 1
}
}
}, {
'$project': {
'candidateName': '$_id.name',
'voteCount': 1,
'_id': 0
}
}, {
'$sort': {
'voteCount': -1
}
}
]
The $addfields the vote fields into an array,
The $unwind separates the documents based on the votes,
The $group gets the voteCount, it will get the total votes regardless of whether they were the first or second vote
The $project is just to clean things up
And the $sort is to sort the array from most votes to least.
I think you'd be best suited by changing your schema, vote1 and vote2 store practically the same information so you could simplify by having a schema where you store votes as an array of objects:
VotePollSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
votes: [{
desc: String,
type: mongoose.Schema.Types.ObjectId,
ref: "Candidate",
type: String,
}],
isActive: {
desc: "is Active.",
type: Boolean,
default: true,
required: true,
},
});
Then you could avoid the $addFields at the start
I have an aggregation pipeline which supposed to return an ordered array. However. Every time I make an API cal, it returns a different order each time. Here is my code
exports.getResultToCompetition = asyncHandler(async (req, res, next) => {
const data = await Result.aggregate([
{
$match: { competition: mongoose.Types.ObjectId(req.params.competitionId) },
},
{
$project: {
points: 1,
kilogramms: 1,
sector: 1,
user: 1,
competition: 1,
place: 1,
},
},
{
$sort: {
kilogramms: -1,
},
},
{
$lookup: {
from: "users", // must be the PHYSICAL name of the collection
localField: "user",
foreignField: "_id",
as: "user",
},
},
{
$addFields: {
user: {
$arrayElemAt: ["$user", 0],
},
},
},
{
$group: {
_id: "$sector",
res: {
$push: "$$ROOT",
},
},
},
]);
res.status(200).json({ success: true, data });
});
And the Schema:
const mongoose = require('mongoose');
const ResultSchema = new mongoose.Schema({
competitionSeries: {
type: String,
default: 0,
},
points: {
type: Number,
default: 0,
},
sector: {
type: String,
},
kilogramms: {
type: Number,
default: 0,
},
place: {
type: Number,
default: 0,
},
isCompetitionRunning: {
type: Boolean,
default: true,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
team: {
name: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
partner: {
type: String,
},
teamName: {
type: String,
},
},
competition: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Competition',
required: true,
},
});
module.exports = Result = mongoose.model('Result', ResultSchema);
The kilograms field is saved as a Double in the MongoDB database
I set the value in the frontend in React as a number.
I am not sure if its causing the problem.
The kilograms value supposed to be a float number like 14.5
This is the result which is correct:
{
"success": true,
"data": [
{
"_id": "B",
"res": [
{
"_id": "5eca12e2607c2c0017c16904",
"points": 1,
"kilogramms": 90.52,
"place": 59,
"sector": "B",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb82c89e5d6cc0017e71a2a",
"avatar": "placeholder.jpg",
"role": "user"
}
},
{
"_id": "5eca1406607c2c0017c16918",
"points": 2,
"kilogramms": 39.45,
"place": 10,
"sector": "B",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb83175e5d6cc0017e71a42",
"avatar": "653571a228226499806e8a717cf2d6e1",
"role": "user"
}
},
{
"_id": "5eca11fc607c2c0017c168f5",
"points": 3,
"kilogramms": 29.62,
"place": 8,
"sector": "B",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb83970e5d6cc0017e71a64",
"avatar": "1589316331809-feederuser",
"role": "user"
}
},
{
"_id": "5eca1250607c2c0017c168fa",
"points": 4,
"kilogramms": 24.25,
"place": 56,
"sector": "B",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb83a00e5d6cc0017e71a67",
"avatar": "placeholder.jpg",
"role": "user"
}
}
]
},
{
"_id": "C",
"res": [
{
"_id": "5eca12cc607c2c0017c16903",
"points": 1,
"kilogramms": 72.43,
"place": 52,
"sector": "C",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb83c2ee5d6cc0017e71a70",
"avatar": "56065850e67a64878e5034d06cecc0ea",
"role": "user"
}
},
{
"_id": "5eca1307607c2c0017c16907",
"points": 2,
"kilogramms": 43.35,
"place": 12,
"sector": "C",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb998864d62090017231ce6",
"avatar": "placeholder.jpg",
"role": "user",
"name": "Halász Tibor"
}
},
{
"_id": "5eca1325607c2c0017c16909",
"points": 3,
"kilogramms": 39.43,
"place": 15,
"sector": "C",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb9b46c4d62090017231cf8",
"avatar": "placeholder.jpg",
"role": "user"
}
},
{
"_id": "5eca14c1607c2c0017c16927",
"points": 4,
"kilogramms": 36.99,
"place": 11,
"sector": "C",
"competition": "5eb90caa7b45620017a4d919",
"user": {
"_id": "5eb82fdbe5d6cc0017e71a3d",
"avatar": "placeholder.jpg",
"role": "user"
}
}
]
}
]
}
After I call again the endpoint it shows a different order. Meaning, Sector C is coming first which is not correct because in Sector B there is a higher kilograms result.
You can add a maxKilogramm field to your documents, and sort by that field by adding the following stages to the end of your aggregation.
{
$addFields: {
"maxKilogramm": {
$max: "$res.kilogramms"
}
}
},
{
$sort: {
maxKilogramm: -1,
},
},
{
$project: {
maxKilogramm: 0
}
}
Playground
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.