Using cond in mongo aggregate match expression - node.js

So i'm trying to query products based on the user specified filter. if the user specifies the size, for example XS, then i fetch all the products with 'XS' sizes, otherwise if the size isn't specified then i will fetch all the products without checking for their size. How can i do it with the mongo aggregation ?
here's my code.
1st: here's 2 products documents from my collection
[
{
"_id": "6124d16b1396d20aea617d75",
"username": "josh988",
"userpic": "josh.jpg",
"adresse": "545 Strawberry Lane Mokena, IL 60448",
"city": "Paris",
"country": "France",
"product": {
"_id": "62c064103a7b5160e957764c",
"images": [
"img1734116er1111111.jpeg"
],
"name": "White shirt",
"category": "Kids",
"subCategory": "Tshirts",
"size": "XS",
"price": 150
}
},
{
"_id": "6124d16b1396d20aea617d75",
"username": "laurel22",
"userpic": "laurel.jpg",
"adresse": "545 Strawberry Lane Mokena, IL 60448",
"city": "Lyon",
"country": "France",
"product": {
"_id": "62c064103a7b5160e957764c",
"images": [
"img1711656775696063.jpeg"
],
"name": "Black shirt",
"category": "Kids",
"subCategory": "Tshirts",
"size": "M",
"price": 250
}
},
]
to make the matter simple , my body api body request looks like this
{
"selectedCategory": "Kids",
"selectedSubCategory": "Tshirts",
// for e.g selectedSize could be XS
"selectedSize": null
}
So what i'd like to do is, if the user select a size then fetch the products with it otherwise fetch everything.
I tried this with mongo but didn't work
const searchResults = await user.aggregate([
{
$match: {
"product.category": `${req.body.selectedCategory}`,
"product.subCategory": `${req.body.selectedSubCategory}`,
$expr: {
$cond: {
// what should i do here ? or is there another way of achieving this
if: { $ne: [`${req.body.selectedSize}`, null] },
then: { product.size : req.body.selectedSize },
// and what should write here to fetch everything
else: { product.size: "" },
},
},
},
},
]);

You can generate the aggregation pipeline conditionally in nodejs itself. Like this:
let matchStage = {
$match: {
"product.category": `${req.body.selectedCategory}`,
"product.subCategory": `${req.body.selectedSubCategory}`,
}
};
if(req.body.selectedSize) {
matchStage = {
$match: {
...matchStage["$match"],
"product.size": request.body.selectedSize
}
};
}
const searchResults = await user.aggregate([matchStage]);

Related

mongodb pull nested array of objects

I want to pull multiple objects from array.
Here is my sample collection:
Users
{
"_id": "wef324DGSshf",
"userTypes": [
{
"type": "students",
"users": [
{
"name": "John",
"age": 20
},
{
"name": "Mike",
"age": 20
},
{
"name": "Henry",
"age": 30
},
{
"name": "Henry",
"age": 40
}
]
}
]
}
I need to pull those objects where:
type: "students" and ages: [20,40]
So I have these 2 inputs: type & ages
Expected Response:
{
"_id": "wef324DGSshf",
"userTypes": [
{
"type": "students",
"users": [
{
"name": "Henry",
"age": 30
}
]
}
]
}
I have tried this query so far but it is not working:
Users.update({
"userTypes.type": "students",
"userTypes.users.age": {$in: [20, 40]},
},
{
$pull: {
"userTypes": {
"userTypes.users.$.age": {$in: [20, 40]}
}
}
});
Can anyone help me what I am doing wrong here?
Use an arrayFilters to specify the filtering for "type": "students" and normally perform $pull on age
db.collection.update({},
{
"$pull": {
"userTypes.$[ut].users": {
"age": {
$in: [
20,
40
]
}
}
}
},
{
arrayFilters: [
{
"ut.type": "students"
}
],
multi: true
})
Mongo Playground
Explanation: Check out the official doc about arrayFilters. You can think of the entries in arrayFilters as predicates. For a variable ut, it needs to have type: students. Let's go back to the $pull part. The predicate is applied to userTypes. That means for an entry in userTypes, ut, it needs to fit in the predicate of type: students. At the same time, we are $pulling the entries that age is in [20, 40].

Posting an aggregation as a new document/collection

In my client I have a form that is sent and stored in Mongo. Made an aggregation to get the name of the people that selected a same place, date and time. Now I would like to create a Mongo document containing all matches as collections so whenever there is a match in place, date and time of people you can get it in a collection. This is what I have so far:
router.get('/match', async (req, res) => {
const matchs = await Forms.aggregate([
{
$group: {
_id: { Date: "$date", Time: "$time", Place: "$place" },
Data: { $addToSet: {Name: "$firstName", Surname:"$surname"}},
count: { $sum: 1 }
}
},
{
$match: {
count: { $gte: 2}
}
},
]);
res.json(matchs)
});
This is the result that I would like to store in Mongo:
{
"_id": {
"Date": "2022-04-20",
"Time": "15:00",
"Place": "Mall"
},
"Data": [
{
"Name": "Carl",
"Surname": "Man"
},
{
"Name": "Christian",
"Surname": "Max"
}
],
"count": 2
}
{
"_id": {
"Date": "2022-04-20",
"Time": "13:00",
"Place": "Restaurant"
},
"Data": [
{
"Name": "Felix",
"Surname": "Sad"
},
{
"Name": "Liu",
"Surname": "Lam"
}
],
"count": 2
}
You can use $out as the last stage in your pipeline. In the following example, matching_collection will contain the result of your pipeline.
{ $out : "matching_collection" }
https://www.mongodb.com/docs/v4.2/reference/operator/aggregation/out/
You can also check $merge, it could be helpful as well.

I need to push into nested array in node mongoose

I used node mongoose.
I need to update this array push new item into Breackfast(mealList.foodList.breackfast || any),
I want to add new foodlist by time can you please give me suggestion for how to do,
{
"_id": "5fe43eb44cd6820963c98c32",
"name": "Monday Diet",
"userID": "5f225d7458b48d0fe897662e",
"day": "Monday",
"type": "private",
"mealList": [
{
"_id": "5fe43eb44cd6820963c98c33",
"time": "Breakfast",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c34",
"foodName": "Eggs",
"Qty": "2",
"calories": "calories",
"category": "category"
}
]
},
{
"_id": "5fe43eb44cd6820963c98c36",
"time": "Lunch",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c37",
"foodName": "food1",
"Qty": "100g"
},
]
}
],
"createdAt": "2020-12-24T07:09:40.141Z",
"updatedAt": "2020-12-24T07:09:40.141Z",
"__v": 0
}
I tried:
Diet.updateOne(
{ "Diet.mealList._id": req.body.mealId },
// { $push: { "Diet.0.mealList.$.foodList": req.body.foodList } },
{ $push: { foodList: req.body.foodList } }
)
Few Fixes:
convert your string _id to object type using mongoose.Types.ObjectId
remove Diet from first and push object in foodList
Diet.updateOne({
"mealList._id": mongoose.Types.ObjectId(req.body.mealId)
},
{
$push: {
"mealList.$.foodList": req.body.foodList
}
})
Playground

fetch array based length - mongoose

I have visitors array. Based on length I want to fetch first 40(highest visted) videos. Is there any query in mongoose for this?
"videos": [],
"description": "Runs for indai",
"status": 1,
"_id": "5e68ee512d3fe53a4426fea5",
"likes": [],
"visited": [{
"_id": "5e690d28797f5b05e066104d",
"user": "::1"
},
{
"_id": "5e690d14797f5b05e066104c",
"user": "::1"
},
{
"_id": "5e690cf7797f5b05e066104b",
"user": "::1"
}],
"comments": [],
"embed": "https://wwdalkfa.com/idaa/46221",
"category": "Mathrubhumi",
"title": "Bdis idnc - Aria.",
"link": "https://www.mdaoa.com/video/46221/dafa",
"image": "https://www.mdaoa.com/media/videos/dstmb1/46221/1b.jpg",
"keywords": "adjal","DAfa",
"__v": 0
controller
exports.getTrendingVideos = async (req, res) => {
try {
const videos = await Video.find().limit(40);
res.send(videos);
} catch (error) {}
};
First, get the length of the array and then sort it, You can do it like this
const videos = await Video.aggregate([
{ $project: { visitedCount: { $size: '$visited' } } },
{ $sort: { $visitedCount: -1 } },
]).limit(40);

Using Mongoose JS to get array of pointer values for document, and expand return

What I am trying to do is the following.
I have objects that could live in multiple documents, and although I am not sure if this is best practice, I am using their ObjectId as pointers.
Document A
{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [
"51a81d8ee4b084336fea2d33",
"asdfsdfe4b084336fea2d33"
]
}
Document B
{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [
"51a81d8ee4b084336fea2d33",
"123454b084336fea2d33"
]
}
The individual artifact looks something like. If this artifact changes then there is no need to update the documents that it lives in:
{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}
What I'd like to do, is get an expanded list of all the artifacts shown in either doc A or doc B in an array something like:
[{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
},
{
"_id": {
"$oid": "123455e4b084336fea2d33"
},
"title": "Artifact B",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}]
The way I have done it in the past is by POSTing from client a list of Ids and then using
{_id: { $in: userQuestArray} }
but if i am constantly posting back to node from the client it seems a bit inefficient. Especially if the array I am posting is hundreds of items long.
Mongoose provide you a feature population which exactly does the same thing you are looking for http://mongoosejs.com/docs/populate.html. In your case what you need to do is
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var tempSchema = Schema({
title : String,
artifacts : [{ type: Schema.Types.ObjectId, ref: 'Artifacts' }]
});
var artifactsSchema = Schema({
title : String,
media : { player: String, source: String, }
});
var tempModel = mongoose.model('Temp', tempSchema);
var artifactsModel = mongoose.model('Artifacts', artifactsSchema);
Now whenever artifacts documents gets created you need to push that in respective temp dcoument artifacts array as shown in mongoose documentation. You can retrive it like that
tempModel
.find(query)
.populate('artifacts')
.exec(function (err, results) {
if (err)
console.log(err);
console.log(results);
})
//results contains data in form shown below
[{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
},
{
"_id": {
"$oid": "123455e4b084336fea2d33"
},
"title": "Artifact B",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}]
}]

Resources