Mongodb: When do we need to expire shopping carts? - mongodb

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.

Related

Aggregate method for each document resulting from find method in 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},
});
});

Change field in object in array of object

I have a field achivment with an array of objects. I need to update the field currentPoints in one object that I will find by field name in the array.
Code model of mongoose:
const achive = new Schema(
{
achiveId: ObjectId,
name: { type: String, required: true },
finishedPoints: { type: Number, required: true },
currentPoints: {
type: Number,
default: 0,
set: function (v) {
if (v >= this.finishedPoints) this.isFinished = true;
return v;
}
},
isFinished: { type: Boolean, default: false }
},
{ _id: false }
);
const achivesSchema = new Schema({
userId: ObjectId,
achivement: [achive]
});
Code query:
export async function progressAchive(req, res) {
const value = 3;
try {
const test = await Achives.updateOne(
{
userId: req.user._id,
achivement: { $elemMatch: { name: req.params.nameAchive } }
},
{ $set: { achivement: { currentPoints: value } } },
{ new: true }
);
res.json(test);
} catch (e) {
console.log(e);
}
}
Instead of updating, it removes all objects from the array and leaves them one object with the currentPoint field. How can I update this like I want?
You should use the following for update
const test = await Achives.updateOne(
{
userId: req.user._id,
},
{
$set:{"achivement.$[el].currentPoints": value}
},
{
arrayFilters:[{
"el.name": req.params.nameAchive
}],
new: true
}
);

set empty values in MongoDB

I'm trying to set some values for the devices that don't have that parameters but for dataPersist and each of the timestamps is not working. I don't know if it'a a problem of the if structure I'm using, but for dataContainer and dataImageList is working.
public getDeviceById = (deviceId: string): Promise<IDevice> => {
return new Promise((resolve, reject) => {
Device.findOne({ _id: mongoose.Types.ObjectId(deviceId) }).then((doc: any) => {
if (doc) {
if (!doc.dataPersist) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataPersist: { persistSize: "", timestamp: new Date()} }})
}
if (!doc.dataContainer) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataContainer: { containerInfo:[], dockerInfo: [], timestamp: new Date()} }})
}
if(!doc.dataImageList) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataImageList: { imageList:[], timestamp: new Date()} }})
}
resolve(doc);
} else {
reject("fieldNotFoundError,device,getDeviceById");
}
}).catch((err: any) => {
console.error(err);
reject("databaseError,043");
});
});
}
Why is that?
This is my model:
import mongoose from "mongoose";
import { IDevice } from "./iDevice";
interface IDeviceModel extends IDevice, mongoose.Document {
}
const deviceSchema = new mongoose.Schema({
dataPersist: {
persistSize: String,
timestamp: Date
},
dataImageList: {
imageList: Array,
timestamp: Date
},
dataContainer: {
containerInfo: Array,
dockerInfo: Array,
timestamp: Date
}
});
const Device = mongoose.model<IDeviceModel>("device", deviceSchema);
export = Device;
Thank in advance for your help.

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})});

Sum object key by key with Mongo on Node

I'm trying to group all this object in one, the idea is to combine all the object.
My function know is like this:
app.get('/stats/:id(\\d+)/weapon/:weapon', function(req, res, next) {
db.collection('stats').aggregate( [
{ $match: { _id: parseInt(req.params.id, 10) } },
{ $unwind: "$session" },
{ $addFields: { weapon: { $objectToArray: '$session.weapons.' + sanitize(req.params.weapon) }, _id: false } },
{ $addFields: { weapon: { $arrayToObject: "$weapon" } } },
{ $project: { weapon: "$weapon", _id: false } }
], function(err, doc) {
if( !err ) {
res.json(doc);
}
else {
console.log(err);
res.end();
}
});
});
and return something like this:
[
{
"weapon":{
"shots":30,
"hitbox":{
"head":7,
"chest":4
},
"kills":4,
"dmg":590
}
},
{
"weapon":{
"shots":46,
"kills":4,
"hitbox":{
"head":3,
"chest":4,
"stomach":3,
"left_leg":2
},
"hs":3,
"dmg":479
}
},
{
"weapon":{
"shots":30,
"hitbox":{
"head":7,
"chest":4
},
"kills":4,
"dmg":590
}
}
]
My idea is to return only one instance of weapon with the sum key by key.
I already try $group and concat array but i can't get the result that i want...
I want like this:
[
{
"weapon":{
"shots":160,
"hitbox":{
"head":17,
"chest":12,
"stomach":3,
"left_leg":2
},
"kills":12,
"hs":3,
"dmg":1659
}
}
]