Aggregate method for each document resulting from find method in mongodb - mongodb

I want to execute this aggregate query:
db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: {
query: 'night',
path: {
wildcard: '*',
},
},
},
},
])
})
for each document resulting from my find method:
here is my find method:
app.get('/', (req, res) => {
db.collection('subs').find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
)
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
I know I probably have to use forEach and create a function, but I couldn't find out what to put in this function, I assume ## Heading ##it should be something like that:
.find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
).forEach(()=>{})
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});

You can achieve this in several different ways, here is the simplest code sample I could produce:
app.get('/', async (req, res) => {
const result = await db.collection('subs').find(
{name: {$regex: 'dexter', $options: '$i'}},
{projection: {_id: 0, content: 0}}
).toArray();
const finalResults = await Promise.all(result.map(async (each) => {
each.textSearchResults = await db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: { // decide what your query is based on each document
query: each.name,
path: {
wildcard: '*',
},
},
},
},
])
return each
}))
res.json({
length: result.length,
body: {result},
});
});

Related

Updating array of objects in Mongoose

I can't handle updating array of objects in my database, tried many options but nothing worked. Im pretty sure that the answer is obvious, but I couldn't manage it since wednesday.
Here is my kitSchema:
const kitSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
kit: {
type: Array,
required: true,
},
profiles: {
type: Array,
required: true,
},
});
module.exports = mongoose.model("Kit", kitSchema);
All users have their own document, and there are also profiles in it. I want to update single profile by passing the id of user and id of profile.
Example of data:
_id: 1,
email: "abc#mail",
password: "abc",
profiles: [
{
id: 1,
name: John
},
]
And here's my latest solution which doesn't work:
router.put("/profile/:id", async (req, res) => {
let kit = await Kit.findById(req.params.id, (error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
});
try {
await kit.profiles.findOneAndUpdate(
{ id: req.body.id },
{ name: req.body.name },
{ new: true },
(error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);
try {
res.status(202).json({ message: "Changed" });
} catch (err) {
res.status(400).json({ message: err });
}
} catch (err) {
res.status(400).json({ message: err });
}
});
Could you give me a hand with this?
As always, after days of trying I've got answer 10 minutes after asking question. Here's what I came up with:
router.put("/profile/:id", async (req, res) => {
await Kit.findOneAndUpdate(
{ _id: req.params.id, profiles: { $elemMatch: { id: req.body.id } } },
{
$set: {
"profiles.$.name": req.body.name,
"profiles.$.profilePicture": req.body.profilePicture,
},
},
{ new: true, safe: true, upsert: true },
(error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);
try {
res.status(202).json({ message: "Changed" });
} catch (err) {
res.status(400).json({ message: err });
}
});

Mongoose - Update/Find Specific Object in an Array Not Working As Expected

I am following the docs without luck and am at a standstill while trying to update an object in an object in an array using MongoDB and Mongoose.
Here is my document:
{
fields: [
{ id: 603d63086db2db00ab09f50f, data: [Object] },
{ id: 603d63086db2db00ab09f510, data: [Object] },
{ id: 603d63086db2db00ab09f511, data: [Object] },
{ id: 603d63086db2db00ab09f512, data: [Object] },
{ id: 603d63086db2db00ab09f513, data: [Object] },
{ id: 603d63086db2db00ab09f514, data: [Object] },
{ id: 603d63086db2db00ab09f515, data: [Object] }
],
layouts: [],
_id: 603d631a6db2db00ab09f517,
bandId: '603d63146db2db00ab09f516',
eventType: 'private',
ownerId: '6039354906410800c14934c1',
__v: 0
}
I am trying to updateOne of the fields.data in the fields array. fields.data is an object as well.
I call my Express/Node Backend to this route.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.updateOne(
{ _id: req.params.id, "fields.id": req.body.id },
{
"$set": {
"fields.$.data": req.body.data,
},
}
);
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
Where req.body is:
{ id: '603d63086db2db00ab09f50f', data: { type: 1, rate: '200.30' } }
**Just in case it's helpful, here is what one of the fields objects looks like in the document,
{"id":"603d63086db2db00ab09f50f","data":{"type":1,"rate":300}}
I have even tried changing my route to find this document - which I have confirmed exists - Truly at a loss why it won't find the document.
Here is how I changed the above route to find the document.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.find(
{ _id: req.params.id, "fields.id": req.body.id },
);
console.log(updating) //returns []
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
The Model
//Create Schema - QG
const QuoteGeneratorSchema = new Schema({
bandId: {
type: String,
required: true,
},
ownerId: {
type: String,
required: true,
},
fields: {
type: Array,
default: defaultFields,
required: true,
},
eventType: {
type: String,
required: false,
},
layouts: {
type: Array,
required: false,
},
});
let QuoteGenerator = mongoose.model("QuoteGenerator", QuoteGeneratorSchema);
module.exports = QuoteGenerator;
Any nudge in the right direction to replacing that data object with a new data object would be extremely helpful! Thanks!

Can Update document with mongodb query but not work when do in mongoose [duplicate]

This question already has answers here:
Update nested subdocuments in MongoDB with arrayFilters
(2 answers)
Closed 3 years ago.
My collection is like this: https://mongoplayground.net/p/91InBXrUq7R
With this query I can update replies.likes
db.getCollection("posts").updateOne(
{
"_id": ObjectId("5da832caeb173112348e509b"), //posts._id
"comments.replies._id":ObjectId("5db6a88f7c6cfb0d0c2b689b"),//replies._id
},
{ "$push": { "comments.$[outer].replies.$[inner].likes": "10000012" } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5db06e11d0987d0aa2cd5593") },//comments._id
{ "inner._id": ObjectId("5db6a88f7c6cfb0d0c2b689b") }//replies._id
]
}
)
But when I code using mongoose, express, collection not update
//Like Reply toggle
router.post("/toggleLikeReply", function(req, res, next) {
var id_post = req.body.id_post;
var id_comment = req.body.id_comment;
var id_reply = req.body.id_reply;
var id_user = req.user._id;
console.log("id_post: "+id_post+" id_comment: "+id_comment+" id_reply: "+id_reply+" id_user: "+id_user);
//todo
Post.aggregate([
{ $match: {_id: ObjectId(id_post),"comments._id": ObjectId(id_comment)}},
{ $unwind: "$comments"},
{ $match: { "comments._id": ObjectId(id_comment)}},
{ $project: {"replies": "$comments.replies", _id: 0}},
{ $match: { "replies._id": ObjectId(id_reply)}},
{ $project: {"likes": "$replies.likes", _id: 0}},
]).exec((err, users_liked) => {
var index = users_liked[0].likes[0].indexOf(id_user);
console.log(users_liked[0].likes[0]);
//todo
if (index == -1) {
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $push: {"comments.$[outer].replies.$[inner].likes": ObjectId(req.user._id)} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("error", error);
}
};
updatePost().then(function(data) {res.send({ like: true, success: true})});
}else{
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $pull: {"comments.$[outer].replies.$[inner].likes": ObjectId(req.user._id)} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("💥", error);
}
};
updatePost().then(function(data) {res.send({ like: false, success: true})});
}
})
});
I logged the all the id is come and the same as I did with mongo query directly .
id_post: 5da832caeb173112348e509b
id_comment: 5db06e11d0987d0aa2cd5593
id_reply: 5db6a88f7c6cfb0d0c2b689b
id_user: 5da85558886aee13e4e7f044
What is wrong with my code using mongoose and express?
Try This Query
var mongoose = require('mongoose');
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $push: {"comments.$[outer].replies.$[inner].likes": req.user._id} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("error", error);
}
};
updatePost().then(function(data) {res.send({ like: true, success: true})});

like/dislike mongodb using bulk

let bulk = Card.collection.initializeOrderedBulkOp();
// if user doesn't exist in array
bulk.find({
'_id': mongoose.Types.ObjectId(card_id),
'likedBy': { '$ne': mongoose.Types.ObjectId(user_id) }
}).updateOne({
'$inc': { 'likes': 1 },
'$push': { 'likedBy': mongoose.Types.ObjectId(user_id) }
});
// if user exists in array
bulk.find({
"_id": mongoose.Types.ObjectId(card_id),
"likedBy": mongoose.Types.ObjectId(user_id)
}).updateOne({
"$inc": { "likes": -1 },
"$pull": { "likedBy": mongoose.Types.ObjectId(user_id) }
});
bulk.execute(function(response) {
console.log(response);
return res.json({
'state': true,
'msg': 'Successful',
})
});
The above is supposed to behave by incrementing or decrementing the likes field if the user id exists in the likedBy array.
However, both functions run, thus the last of the bulk gets to be the last action done. In the above, the end result is always zero.
I suspect the query matches a document always, thus the .updateOne() parts run on all.
Here's the schema:
var CardSchema = new Schema({
slug: {
type: String,
lowercase: true,
index: true
},
title: String,
content: String,
createdAt: {
type: Date,
default: Date.now,
},
updatedAt: {
type: Date,
},
options: [],
likedBy: [],
likes: Number,
createdBy: String,
featured: Boolean,
});
Is there a better mongo way to do the like/dislike thing?
Going with this for now. Too verbose, but works. I've created a like and dislike button separately in the UI, which calls two independent functions, but to same endpoint, with endpoint rigged like this:
let like = req.body.like;
// if request is a like
if (like) {
Card.update({
'_id': mongoose.Types.ObjectId(card_id),
'likedBy': { '$ne': mongoose.Types.ObjectId(user_id) }
}, {
'$inc': { 'likes': 1 },
'$push': { 'likedBy': mongoose.Types.ObjectId(user_id) }
}, function(err) {
if (err) {
console.log(err);
return res.json({
'state': false,
'msg': err
})
}
return res.json({
'state': true,
'msg': 'Liked',
})
})
} else if (!like) { // if request is dislike
Card.update({
'_id': mongoose.Types.ObjectId(card_id),
'likedBy': mongoose.Types.ObjectId(user_id)
}, {
'$inc': { 'likes': -1 },
'$pull': { 'likedBy': mongoose.Types.ObjectId(user_id) }
}, function(err,) {
if (err) {
console.log(err);
return res.json({
'state': false,
'msg': err
})
}
return res.json({
'state': true,
'msg': 'Disliked',
})
})
}
Then something like this makes the request,
likeCard(card_id: string, like: boolean) {
let param = {
card_id: card_id,
like: like
};
return this.http.post(AppSettings.API_ENDPOINT + '/card/like', JSON.stringify(param), { headers: this.headers })
.map((res) => {
return res
})
}

Mongodb: When do we need to expire shopping carts?

I'm building an e-commerce website by ExpressJs + Mongodb and I'm stuck with this concern:
When do we need to expire the cart ( remove the cart and return the product to inventory ) technically ? Whenever user visit the cart? or should I need a cron job?
I've followed this article: https://www.infoq.com/articles/data-model-mongodb
Here's my cart model's implementation:
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CartItem = new Schema({
product: { type: Schema.Types.ObjectId, ref: 'Product' },
quantity: Number
});
const Cart = new Schema({
userSessionId: String,
status: {
type: String,
enum: [ 'active', 'completed', 'expiring', 'expired' ],
default: 'active'
},
items: [ CartItem ],
modifiedOn: { type: Date }
});
Cart.static({
summary: function(params, cb) {
this.aggregate([
{
$match: { userSessionId: params.userSessionId }
},
{
$unwind: {
path: '$items'
}
},
{
$lookup: {
from: 'products',
localField: 'items.product',
foreignField: '_id',
as: 'product'
}
},
{
$unwind: {
path: '$product',
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: { userSessionId: '$userSessionId' },
count: { $sum: '$items.quantity' },
total: { $sum: { $multiply: [ '$product.price', '$items.quantity' ] } }
}
}
], (err, results) => cb(err, results[0]));
},
addProduct: function(params, cb, test) {
var d = new Date();
if (test) {
d.setMinutes(d.getMinutes() - 10);
}
this.findOneAndUpdate(
{ userSessionId: params.userSessionId },
{ $set: { modifiedOn: d } },
{ upsert: true, new: true }, (err, cart) => {
if (err) {
return cb(err);
}
const index = cart.items.findIndex((item) => {
return item.product.equals(params.productId);
});
if (index === -1) {
cart.items.push({
product: params.productId,
quantity: params.quantity
});
} else {
cart.items[index].quantity += parseFloat(params.quantity);
}
cart.save(cb);
});
},
updateQuantity: function(params, cb) {
this.findOneAndUpdate(
{ userSessionId: params.userSessionId },
{},
{ upsert: true, new: true }, (err, cart) => {
if (err) {
return cb(err);
}
const index = cart.items.findIndex((item) => {
return item.product.equals(params.productId);
});
if (index === -1) {
return cb(new Error('Can not find product in cart'));
}
cart.items[index].quantity = params.quantity;
cart.save(cb);
});
},
findItem: function(params, cb) {
this.findOne({ userSessionId: params.userSessionId }).exec((err, cart) => {
if (err) {
return cb(err);
}
const index = cart.items.findIndex((item) => {
return item.product.equals(params.productId);
});
if (index === -1) {
return cb(new Error('Can not find product in cart'));
}
cb(null, cart.items[index]);
});
},
removeProduct: function(params, cb) {
this.update(
{ userSessionId: params.userSessionId },
{
$pull: { items: { product: params.productId } },
$set: { modifiedOn: new Date() }
},
cb
);
},
getExpiredCarts: function(params, cb) {
var now = new Date();
if (typeof params.timeout !== 'number') {
return cb(new Error('timeout should be a number!'));
}
now.setMinutes(now.getMinutes() - params.timeout);
this.find(
{ modifiedOn: { $lte: now }, status: 'active' }
).exec(cb);
}
});
mongoose.model('Cart', Cart);
You should use some kind of distributed session to store the shopping cart!
I think you are looking for something like: https://www.youtube.com/watch?v=g32awc4HrLA
It uses expressjs-session and mongodb then you have a distributed cache and it will work with multiple instances of your application.