$group in Mogoose Random Results But No Duplicate in Pagination - node.js

Actually i want to produce random results each time page loads, it's working but on pagination sometimes I got
duplicate results one or two products, am i doing something wrong. i am randomising by spliting product_code. your
help is really appreciated.
var obj = {
$or: [
{
'main_data.product_name': {
$regex: req.body.searchTerm,
$options: 'i'
}
},
{
'main_data.product_code': {
$regex: req.body.searchTerm,
$options: 'i'
}
}
],
'main_data.status': 'active',
}
var query = [
{ $unwind: '$main_material' },
{
$project: {
product_name: 1,
product_code: 1,
main_material: 1,
status:1,
qty_per_box:1,
city_state: { $split: ["$product_code", "-"] },
qty: 1
}
},
{
$project: {
product_name: 1,
product_code: 1,
main_material: 1,
status:1,
qty_per_box:1,
city_state: "$city_state"
}
},
{ $group: { _id: { "pc": "$city_state" }, main_data: { "$first": "$$ROOT" }, cdate: { "$first": "$createdOn" } } },
{ $match: obj},
{ $sort: { "price_usd": 1 } },
{ $skip: parseInt(req.params.skip) },
{ $limit: parseInt(req.body.items) },
]
Products_model.aggregate(query);
My Exact requirement is to show random products every time. But due to random list in
pagination it creates duplicity in the results.

Related

I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too

I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too. For ex consider the below code -
const result = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$sort: { StudentData: -1 }
},
{
$count: 'count'
},
{
$skip: skip
},
{
$limit: limit
}
]
);
Here I want to save two things in the result variable - the number of documents and individually all the documents.
let [{ totalItems, result }] = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$facet: {
result: [
{
$sort: { BirthDate: -1 },
},
{
$skip: skip
},
{
$limit: limit
}
],
totalItems: [{ $count: 'count' }]
}
},
{
$addFields: {
totalItems: {
$arrayElemAt: ["$totalItems.count", 0]
},
}
}
]
);

How to find the latest date in nested array of objects (MongoDB)

I am trying to find the latest "order" in "orders" array in the whole collection (Not only in the one object).
Data:
[
{
_id: 1,
orders: [
{
title: 'Burger',
date: {
$date: '2021-07-18T13:12:08.717Z',
},
},
],
},
{
_id: 2,
orders: [
{
title: 'Salad',
date: {
$date: '2021-07-18T13:35:01.586Z',
},
},
],
},
];
Code:
var restaurant = await Restaurant.findOne({
'orders.date': 1,
});
Rather simple:
db.collection.aggregate([
{ $project: { latest_order: { $max: "$orders.date" } } }
])
If you like to get the full order use this:
db.collection.aggregate([
{
$project: {
latest_order: {
$first: {
$filter: {
input: "$orders",
cond: { $eq: [ "$$this.date", { $max: "$orders.date" } ] }
}
}
}
}
},
{ $sort: { "latest_order.date": 1 } },
{ $limit: 1 }
])
Mongo Playground
You have to use aggregation for that
db.collection.aggregate([
{ $unwind: "$orders" },
{ $sort: { "orders.date": -1 } },
{ $limit: 1 },
{
"$group": {
"_id": "$_id",
"orders": { "$first": "$orders" }
}
}
])
Working Mongo playground

How to count filtered results in $match in mongodb?

I have an aggregation query like this
nSkip=4, count=25, sortOn='first_name' , order=-1, toMatch='biker' // all variables are dynamic
query={status: true, roles: { $regex: toMatchRole, $options: "m" }} // also dynamic
User.aggregate([
{
$match: query
},
// after this I need the total number of documents that matched the criteria,
// before sorting or skipping or limiting in "total_count" variable
{
$sort: {
[sortOn]: order
}
},
{
$skip: nSkip
},
{
$limit: count
},
{
$project: {
last_name: 1,
first_name: 1,
email: 1
}
}
])
User Collection
{
_id:60befdcfa4198332b728f9cd",
status:false,
roles:["biker"],
email:"john#textmercato.com",
last_name:"aggr",
first_name:"john",
}
I am not sure how to achieve this without disturbing the rest of the stages in aggregation. Can someone please help me out.
You can use $group
{
"$group": {
"_id": null,
"data": { "$push": "$$ROOT" },
"total_count": { $sum: 1 }
}
},
{ $unwind: "$data" },
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [ "$$ROOT", "$data" ]
}
}
}
and finally Project the total_count
Working Mongo playground

search from child collection contains the keyword from parent and child collection

I am facing a problem with getting data from database using parent child relationship collections.
Here is my collection structure --
-post
---post cloth : brand id from brands collections
-----brand
Now I am getting data from post and post cloth with keyword search from post cloth and brand table if any key matches from post cloth and brand. Till post cloth it is working fine along with keyword search in or condition, Now I also need to search from brand and return the result if keyword contains in brand as well.
here are my cases --
data returned : if any of post_cloths keys matches the keyword searched
data returned : if any of the post_cloths keys matches the keyword OR lookup with brand name matches the keyword
data returned : if all keys from post_cloths not matches the keyword but lookup with brand name matches the keyword
data not returned : if no keys from post_cloths matches the keyword and also lookup with brand name not matches the keyword
Here is my code :
var page = 0;
if (req.query.page >= 0) {
page = req.query.page;
}
let filter = { 'totalCloth': { $gte: 1 } };
if (req.query.user != null && req.query.user != '') {
filter.createdBy = ObjectID(req.query.user);
}
console.log(filter);
var searchQuery = [];
var brandSearchQuery = [];
if (req.query.keyword != null && req.query.keyword != '') {
console.log(req.query.keyword);
keyword = req.query.keyword;
searchQuery = [
{
$regexFind: {
input: '$category',
regex: new RegExp(keyword),
options: 'i',
},
},
{
$regexFind: {
input: '$color',
regex: new RegExp(keyword),
options: 'i',
},
},
{
$regexFind: {
input: '$country',
regex: new RegExp(keyword),
options: 'i',
},
},
{
$regexFind: {
input: '$size',
regex: new RegExp(keyword),
options: 'i',
},
},
{
$regexFind: {
input: '$clothMaterial',
regex: new RegExp(keyword),
options: 'i',
},
},
];
brandSearchQuery = [
{
$regexFind: {
input: '$name',
regex: new RegExp(keyword),
options: 'i',
},
},
];
} else {
searchQuery = [{}];
brandSearchQuery = [{}];
}
// get the post details
// PostModel.find(filter).countDocuments().then(countPosts => {
PostModel.aggregate([
{
$lookup: {
from: 'post_cloths',
let: { postId: '$_id' },
pipeline: [
//lookup for brand
{
$lookup: {
from: 'brands',
let: { brandId: '$brandId' },
pipeline: [
{
$match: {
$expr:
{
$and:
[
{ $eq: ['$_id', '$$brandId'] },
{ $or: brandSearchQuery },
],
},
},
},
],
as: 'brand',
},
},
//end of brand lookup
{
$match: {
$expr: {
$and:
[
{ $eq: ['$postId', '$$postId'] },
{
$or: searchQuery,
},
],
},
},
},
{
$project: {
totalBrands: { $size: '$brand' },
},
},
{
$match: {
$expr:
{ $or: [{ $match: { totalBrands: { $gte: 1 } } }] },
},
},
],
as: 'postCloth',
},
},
{
$project: {
image: 1,
createdAt: 1,
createdBy: 1,
mediaUrl: {
$concat: [process.env.PROJECT_URL + '/files/', '$image'],
},
totalCloth: { $size: '$postCloth' },
},
},
//check for post cloth object if length is greater than equals to 1
{
$match: filter,
},
{ $skip: 12 * page },
{ $limit: 12 },
{ $sort: { createdAt: -1 } },
]).exec(function(err, post) {
return apiResponse.successResponseWithData(res, 'Successful', post);
});
I am getting data properly, but not while searching from brand. Please suggest how we can search the data from the cases given. there is simple keyword search.
Thanks in advance
The problem is with you're main's $lookup's pipeline:
first you start with the brand $lookup, which i'll assume works ( if you provide schema's for your collections it would be easy to verify), however right after that $lookup you do this:
{
$match: {
$expr:
{
$and:
[
{ $eq: ['$postId', '$$postId'] },
{
$or: searchQuery,
},
],
},
},
},
This means if the searchQuery fails even if a brand exists the document will be filtered out, you should change it to:
{
$match: {
$expr:
{
$and:
[
{ $eq: ['$postId', '$$postId'] },
{
$or: [
{
$or: searchQuery
},
{
$gt: [{$size: "$brand"}, 0]
}
],
},
],
},
},
},
Now this will also matched documents that have any brands in the brand field, meaning the brand matched the nested $lookup, you can then drop the next 2 stages that check for the brand size.
I also recommend that you move the $eq for the postId to the start of the $lookup, this will improve performance immensely, after all the changes the entire pipeline would look like:
PostModel.aggregate([
{
$lookup: {
from: 'post_cloths',
let: { postId: '$_id' },
pipeline: [
{
$match: { $eq: ['$postId', '$$postId'] },
},
{
$lookup: {
from: 'brands',
let: { brandId: '$brandId' },
pipeline: [
{
$match: {
$expr:
{
$and:
[
{ $eq: ['$_id', '$$brandId'] },
{ $or: brandSearchQuery },
],
},
},
},
],
as: 'brand',
},
},
{
$match: {
$expr: {
$and:
[
{
$or: [
{
$or: searchQuery,
},
{
$gt: [{ $size: '$brand' }, 0],
},
],
},
],
},
},
},
],
as: 'postCloth',
},
},
{
$project: {
image: 1,
createdAt: 1,
createdBy: 1,
mediaUrl: {
$concat: [process.env.PROJECT_URL + '/files/', '$image'],
},
totalCloth: { $size: '$postCloth' },
},
},
{
$match: filter,
},
{ $skip: 12 * page },
{ $limit: 12 },
{ $sort: { createdAt: -1 } },
])

mongodb match and group 2 query in one

var oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
User.aggregate([
{ $match: { isAdmin: false, isActive: true } },
{
$group: {
_id: null,
totalCount: {
$sum: 1
}
}
},
])
User.aggregate([
{ $match: { isAdmin: false, dateCreated: { $gte: oneWeekAgo }, isActive: true } },
{
$group: {
_id: null,
lastWeekTotal: {
$sum: 1
}
}
},
])
is there a way to combine 2 aggregation queries above?
I want to count all the entries in the collection and also entries that are created within a week.
Expected result:
[ { _id: null, totalCount: 100 , lastWeekTotal: 10 } ]
You can combine inside $group together like this,
The $cond operator, there are three arguments ($cond: [if check condition, then, else])
first part if condition checks your conditions using $and operator, if conditions is true then return 1 otherwise 0
User.aggregate([
{
$group: {
_id: null,
totalCount: {
$sum: {
$cond: [
{
$and: [
{ $eq: ["$isAdmin", false] },
{ $eq: ["$isActive", true] }
]
},
1,
0
]
}
},
lastWeekTotal: {
$sum: {
$cond: [
{
$and: [
{ $gte: ["$dateCreated", oneWeekAgo] },
{ $eq: ["$isAdmin", false] },
{ $eq: ["$isActive", true] }
]
},
1,
0
]
}
}
}
}
])
Playground

Resources