MongoDB Populate Aggregate Unwind - javascript

I have the following data structure for my user model:
{
_id: object,
name: string,
quotes: [ { quote: string, createdAt: date } ],
friends: [ { _id: object } ] (this refers to the _id from the user model)
}
What i'd like to do is return this:
{
quote: string,
createdAt: date,
_id: object (friend's)
}
{
quote: string,
createdAt: date,
_id: object (friend's)
}
...
sorted by createdAt = recent to oldest

What I understood from your question is you want to unwind two array elements based on recent time.
db.user.aggregate([
{
$unwind: "$quotes"
},
{
$unwind: "$friends"
},
{
$sort: {
"quotes.createdAt": -1
}
}
])

Related

Mongoose and MongoDB: How do I update a nested object with nested arrays?

I wasn't sure how to precisely word the title to describe my data structure, but it looks something like this.
[
{
_id: {
$oid: 231323123
},
OC: {
content: 'asdasfd',
date: '4314',
username: 'asdfasdfasf'
},
replies: [
{
_id: {
$oid: 12331234
},
OC: {
content: 'adfasdfadf',
date: '213124',
username: 'useruser'
},
replies: [
{
_id: {
$oid: 12331234
},
OC: {
content: 'adfasdfadf',
date: '213124',
username: 'useruser'
},
replies: []
}
]
},
{
_id: {
$oid: 3231231232
},
OC: {
content: 'asfasdf',
date: '1231232',
username: 'anotheruser'
},
replies: []
}
]
},
{
_id: {
$oid: 2331233
},
OC: {
content: 'mor econtent',
date: '122112',
username: 'okokok'
},
replies: []
}
]
This is just an example to get the point across, I'm trying to create a nested comment model but I don't know how to update any given replies array within any given nested comment using mongoose with mongoDB.
I was thinking I would do something like this:
CommentModel.updateOne({ _id: body.parentCommentId }, {
$push: {
`${body.referenceString}.replies`: {
new comment object
}
}
})
The problem is, I don't know how to automatically generate a reference string to get any nested comment array and update it with a new comment, or if this is even the correct approach at all. The position and contents of the nested comment would be decided by a front-end user and used to reference a back-end document within the database for update.

Mongoose query - groupBy category and get last 4 items of each category

I am struggling in Writing that fetches 4 Products of each category. What I have done is
exports.recentproducts = catchAsync(async (req, res, next) => {
const doc = await Product.aggregate([
{ $sort: { date: -1 } },
{
$replaceRoot: {
newRoot: {
$mergeObjects: [{ $arrayElemAt: ['$products', 0] }, '$$ROOT'],
},
},
},
{
$group: {
_id: '$productCategory',
products: { $push: '$$ROOT' },
},
},
{
$project: {
// pagination for products
products: {
$slice: ['$products', 4],
},
_id: 1,
},
},
{
$lookup: {
from: 'Shop',
localField: 'shopId',
foreignField: '_id',
as: 'shop',
},
},
]);
Document Model
const mongoose = require('mongoose');
var ProductSchema = mongoose.Schema({
title: {
type: String,
require: [true, 'Product must have a Title!'],
},
productCategory: {
type: String,
require: [true, 'Product must have a Category!'],
},
shopId: {
type: mongoose.Schema.ObjectId,
ref: 'Shop',
require: [true, 'Product must have a Shop!'],
},
});
var Product = mongoose.model('Product', ProductSchema);
module.exports = Product;
expected result---
result= [
{
productCategory: "Graphics",
products:[//4 products object here
{
must populate shop data
}
]
},
{
productCategory: "3d",
products:[//4 products object here]
},
//there are seven categories I have like that
]
The Code i have done is working fine but it has two problems
It does not populate shopId from Shop Model even through I have tried lookup
It does not sort products in descending order(does not sort by date)
There are few fixes in your implemented query,
$sort stage as it is,
$group stage as it is and moves to the second stage
$project stage as it is and move to third stage
$lookup with shop collection, pass products.shopId as localField
$project for merge shop object in products array
$map to iterate loop of products array
$filter to iterate loop of shop array return matching product
$arrayElemAt to get first element from above filtered result
$mergeOjects to merge current object with filtered shop object
const doc = await Product.aggregate([
{ $sort: { date: -1 } },
{
$group: {
_id: "$productCategory",
products: { $push: "$$ROOT" }
}
},
{
$project: {
products: { $slice: ["$products", 4] }
}
},
{
$lookup: {
from: "Shop",
localField: "products.shopId",
foreignField: "_id",
as: "shop"
}
},
{
$project: {
products: {
$map: {
input: "$products",
in: {
$mergeObjects: [
"$$this",
{
shop: {
$arrayElemAt: [
{
$filter: {
input: "$shop",
as: "s",
cond: { $eq: ["$$s._id", "$$this.shopId"] }
}
},
0
]
}
}
]
}
}
}
}
}
])
Playground
Query
in MongoDB 5 we can use $setWindowFields and $rank
partition by productCategory and sort by date descending
keep only rank <= 4 (4 latest products)
lookup to get the shop information
group by category and push all the information of product and shop
Test code here
Product.aggregate(
[{$setWindowFields:
{partitionBy:"$productCategory",
sortBy:{date:-1},
output:{rank:{$rank:{}}}}},
{$match:{rank:{$lte:4}}},
{$lookup:
{from:"Shop",
localField:"shopId",
foreignField:"_id",
as:"shop"}},
{$set:{shop:{$first:"$shop"}}},
{$group:{_id:"$productCategory", products:{$push:"$$ROOT"}}}])

Impossible to query nested mongoose array?

I want to Query and array with regex inside and mongoose (mongoDB) model.
I want to search inside the nested array of the Productmodel :
const productSchema = new schema(
{
name: requiredString,
sku: requiredUniqueNumber,
ean: requiredUniqueNumber,
suppliers: [{ type: mongoose.Schema.Types.ObjectId, ref: SupplierModel }],
categories: [{ type: mongoose.Schema.Types.ObjectId, ref: CategoryModel }],
mainImage: requiredString,
images: [{ type: String }],
description: requiredString,
stock: requiredNumber,
price: requiredNumber,
totalOrders: requiredNumber,
reviews: [review],
},
{
timestamps: true,
count: true,
}
);
The model inside the "suppliers" array is:
const supplierSchema = new schema(
{
supplierName: requiredUniqueString,
invoiceAddress: address,
visitAddress: address,
status: supplierStatusEnum,
contacts: address,
accountType: accountTypeEnum,
logo: requiredString,
user: { type: schema.Types.ObjectId, ref: "products" },
},
{
timestamps: true,
}
);
Now here's the problem, if i query and and populate() i get all the results. But for some reason I cannot search inside the Array containing several suppliers. Here's of what i have:
foundProducts = await ProductModel.find({
$or: [
{
name: {
$regex: regex,
},
},
{
"suppliers.supplierName": {
$regex: regex,
},
},
{
description: {
$regex: regex,
},
},
],
});
The object in JSON:
If he finds that the suppliers model contains the regex he should give back the whole porductmodel containing that supplier.
What is the best way to search in all the items inside of an nested array.
ps. I'm a junior developer comming from PostgreSQL, so bare with me while I'm trying this noSQL "thing" :)
I was doing the wrong query. I need to do
{
"suppliers._id": {
$regex: regex,
},
},
I can only search on _id, since this is the way that i "modeled" it.

Limit in mongoose for one query

My schema:
export const MessagesAllSchema = new Schema({
senderName: {type: String, required: true},
senderId: {type: String, required: true},
content: String,
date: {type: Date, default: Date.now()},
roomId: Schema.Types.ObjectId,
});
My query:
AllMessages.find(
{roomId: [roomId1, roomId2]},
(err, messages) => {
console.log(messages);
},
).sort({date: -1});
My code return
My code returns several messages from room 1 and room 2.
I want to achieve
I want to my code return one message for room 1 and one message for room 2. If I apply .limi(2) I got a 2 message for room 1, but I want to get one message per room.
It is not possible with find(), You can try aggregate() method,
$match roomId in array of roomIds
$group by roomId and get first message form multiple grouped messages in root variable
$replceWith to replace root object in root
$sort by date in descending order
$limit 2 documents only
const messages = await AllMessages.aggregate([
{
$match: {
roomId: { $in: [roomId1, roomId2] }
}
},
{
$group: {
_id: "$roomId",
root: { $first: "$$ROOT" }
}
},
{ $replaceWith: "$root" },
{ $sort: { date: -1 } },
{ $limit: 2 }
]).exec();
console.log(messages);
Playground

Search for a specific item in an array of objects and query if an attribute exists

Sample data:
{
_id: '1234',
games: [{
name: 'World of Warcraft',
lastLogin: ISODate(),
subscriptions: ['56', '78']
}, {
name: 'Starcraft II',
lastLogin: ISODate()
}]
}
Essentially, I want to find everyone that does not have a "subscriptions" field for a given game. I can't quite figure out a good way to do it.
Players.update({
'games.name': 'World of Warcraft',
'games.$.subscriptions': { $exists: false }
}, {
$set: { 'games.$.subscriptions': GLOBAL_SUB }
});
How do you query an array of elements for an attribute and the existence of a field?
Use $elemMatch when you want to match multiple terms on the same array element:
Players.update({
games: { $elemMatch: {
name: 'World of Warcraft',
subscriptions: { $exists: false }
}}
}, {
$set: { 'games.$.subscriptions': GLOBAL_SUB }
});

Categories