how to delete sub doc only on expire using mongoose? - node.js

I have problem deleting subdocument from collection 1, when document in collection 2 is expire.
I have cart schema like below.
var cartSchema = new schema({
userName : {type: String, default: null},
total : {type: Number, default: 0},
qty : {type: Number, default: 0},
productId : {type: String, default: null},
createdAt : {type: Date, default: Date.now, expires: 7200} // expired in 2 hours
});
and here I have schema which save object carted related to cartSchema
var merchantSchema = new schema({
seller : String,
address : String,
email : String,
products : [
{
title : String,
ingredients : String,
qty : Number,
carted : [
{
qty : Number,
cartId : String,
createdAt : {type: Date, default: Date.now}
}
}
],
});
since expireAt query runs automatically, I don't know how to delete automatically carted object when cart expired and deleted.
when cart in cartSchema with id 123 expired and deleted, so cart in carted object with id 123 will also be deleted.
if I add expires property in merchantSchema createdAt : {type: Date, default: Date.now, expires: 7200}, it will delete whole document.

You can't use a TTL index to delete sub-document.
One workaround could be done through change your data schema through population as below
var merchantSchema = new schema({
seller : String,
address : String,
email : String,
products : [productSchema],
});
var Merchant = mongoose.model('Merchant', merchantSchema);
var productSchema = new Schema({
title : String,
ingredients : String,
qty : Number
});
var Product = mongoose.model('Product', productSchema);
var cartSchema = new schema({
userName : {type: String, default: null},
total : {type: Number, default: 0},
qty : {type: Number, default: 0},
productId : {type: Schema.Types.ObjectId, ref: 'Product'},
createdAt : {type: Date, default: Date.now, expires: 7200} // expired in 2 hours
});
Then the createdAt expires, then the cart document will be deleted, and there is no cart information related to product document.

Related

Multiple one-to-many relationship on mongoose

I'm using mongoose to store three models of documents, sometimes I have to update references between then, for this I'm using mongoose-relationship plugin,
My need is reference then like this:
One customer have many schedules,
One costumer have many orders,
One order have many schedules
When I create an order I need to push schedules id's into order to reference then. But I can only reference one childPath per collection, my models are mapped like this;
Customers:
var CustomerSchema = new Schema({
name: {type: String, required: true},
email: {type: String, required: true},
shedules: [{ type:mongoose.Schema.Types.ObjectId, ref:"Schedule" }],
orders: [{ type:mongoose.Schema.Types.ObjectId, ref:"Order" }]
}
Schedules:
var ScheduleSchema = new Schema({
customer: {type:mongoose.Schema.Types.ObjectId, ref:"Customer", childPath:"shedules"}, //shedule id
order: {type:mongoose.Schema.Types.ObjectId, ref:"Order", childPath:"shedules"}, //order Id
sequence: {type: Number, default: 0},
creation_date: {type: Date, default: Date.now}
}
SheduleSchema.plugin(relationship, {relationshipPathName:['customer','order']});
Orders:
var OrderSchema = new Schema({
customer: {type:mongoose.Schema.Types.ObjectId, ref:"Customer", childPath:"order"},
shedules: [{type:mongoose.Schema.Types.ObjectId, ref:"Shedule" }],// <-- this field doesn't update.
price: {type: Number, default: 0}
}
OrderSchema.plugin(relationship, { relationshipPathName:'customer' });

Mongoose subdocument not created in mongoDB but added to parent array

I have a method to update the commerce information. There is the possibility to add Workdays to the commerce by sending them in the request body.
There following code works fine except that the workdays are not created in mongoDB. They are only saved in the Commerce document (as an array od ids) but the Collection called "workday" is not createdin mongoDB. Why it's not created?
if(req.body.workdays){
var workdays = req.body.workdays;
var lunch = req.body.lunch.split("_");
commerce.workdays=[];
for(var i =0, size=workdays.length; i<size; i++ ){
var item=new Workday();
item.dayOfWeek = workdays[i];
item.owner=commerce._id;
var range = new Range();
range.initial = lunch[0];
range.end = lunch[1];
range.workday = item;
item.ranges.push(range);
commerce.workdays.push(item);
}
}
commerce.save(function(err) {
if(!err) {
log.debug('Updated');
res.status(200).send(commerce);
} else {
errorHandler.processError(err, res, log);
}
});
here are the models:
var CommerceSchema = new Schema({
// Common fields.
createdAt : {type : Date, default : Date.now},
location: [Number, Number],
photos: [{type : Schema.Types.ObjectId, ref : 'Photo'}],
name: { type: String},
address: { type: String},
email: { type: String, default: "-"},
workdays: [{type : Schema.Types.ObjectId, ref : 'Workday'}],
description: { type: String, default: "-"},
phone: { type: Number},
user: {type : String, ref : 'User'},
type: [{ type: Number, default: 0}]
});
var WorkdaySchema = new Schema({
dayOfWeek: { type: Number},
owner: {type : String},
ranges: [{type : Schema.Types.ObjectId, ref : 'Range'}],
createdAt : {type : Date, default : Date.now}
});
var RangeSchema = new Schema({
initial: { type: Number},
end: { type: Number},
workday: {type : String, ref : 'Workday'}
});
"workdays" is expecting Mongo ObjectIds. You have to save the individual Workdays first, and then you can add their Ids (_id) to the workdays Array.

Can I populate ref's in a mongoose.js model instead of everytime I query?

Intro: I am creating a StackExchange clone using Node and Mongo to learn the language. I am currently working on the API.
I have the following 'questionSchema':
var questionSchema = new Schema({
_id : {type: String, default: shortid.generate},
title : {type: String, required: true},
question : {type: String, required: true},
user : {type: Schema.ObjectId, ref: 'User'},
points : {type: Number, default: 0},
date : {type: Date, default: Date.now},
answers : [answerSchema],
votes : [{
user: {type: Schema.ObjectId, ref: 'User', required: true},
vote: {type: Number, enum: [-1,0,1]}
}],
__v : {type: Number, select: false}
});
The idea is that when a user votes on a question the points field is incremented (or decremented) and the userid and vote added to the votes array. I have the vote array to detect if the user has already voted and prevent additional votes.
The problem: I'm having trouble actually checking if the user has voted (checking if their userid exists in the votes array). I have been playing around with adding the method 'hasVoted' to the questionSchema but:
I'm not sure how to actually make the check happen.
I'm also not sure if there is a way for me to filter the votes array during the query (at MongoDB) instead of after node gets the results.
This is my attempt at the method which I know is wrong:
//Has the user already voted on this question?
questionSchema.methods.hasVoted = function (userid, cb) {
this.votes.filter(function(vote) {
if(userid == vote._id) {
return '1';
} else {
return '0';
}
});
};
I would recommend to make vote schema like so
var voteSchema = new Schema({
user: {type: Schema.ObjectId, ref: 'User', required: true},
vote : {type: Number, required: true}
})
var questionSchema = new Schema({
_id : {type: String, default: shortid.generate},
title : {type: String, required: true},
question : {type: String, required: true},
points : {type: Number, default: 0},
date : {type: Date, default: Date.now},
answers : [answerSchema],
votes : [{type: Schema.ObjectId, ref: 'Vote', required: false}]
});
Then just get your question and go through all the votes.
QuestionSchema.findById(question.id)
.populate('votes')
.exec(function (err, question) {
// go through all the votes here
}
or query if there is an question with your user id inside the votes
QuestionSchema.find()
.and([{_id:questionId,},{votes.user:userId}])
.populate('votes') //dunno if you really have to populate i think you don't have to
.exec(function (err, user) {
// check if(user)
}
or do it like described here findOne Subdocument in Mongoose
//EDIT
or if you don't change your schema
QuestionSchema.find({votes.user:userId})
.exec(function (err, user) {
// should return ALL questions where the user id is in the votes your want a specific question do it in a and like in the example above
}
and if you only want that one element from the array you have to make a projection like described here How to find document and single subdocument matching given criterias in MongoDB collection

Node.js - Create Relationships with Mongoose from different file

I have two Schemas Products and Users in different files. Products are belong to User and User have many Product
The Problem is, I have try to use Populate but somehow it return not what I expected.
here is my Schema for Product on models products.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var users = require('../models/users');
var Product = mongoose.Schema({
user_id : {type: String, required: true },
category_id : {type: String, default: null},
title : {type: String, required: true },
content : {type: String, default : "" },
pictureUrls : Array,
counter : {type : Number, default : 0},,
lowercaseTitle : {type: String, default : null },
_user : { type: Schema.Types.ObjectId, ref: users.User }
});
Product.set('toObject', {
getters: true,
virtuals: true
});
module.exports = mongoose.model('Product', Product)
and this is my Schema for User on models users.js
var mongoose = require('mongoose');
var User = mongoose.Schema({
firstName : {type: String, default: "" },
lastName : {type: String, default: "" },
username : {type: String, required: true },
email : {type: String, required: true },
password : {type: String, default: "" },
bio : {type: String, default: "" },
website : {type: String, default: "" },
phone : {type: String, default: "" },
gender : {type: String, default: "" },
birthDate : {type: Date, default: null },
avatarUrl : {type: String, default: "" },
verified : {type : Boolean, default : false}
});
User.set('toObject', {
getters: true,
virtuals: true
});
module.exports = mongoose.model('User', User);
currently I am using method find by calling each Models
User.findOne({"sessionToken" : bearerHeader}, function (err, user){
Product.find({"user_id" : user._id}, function (err, products){
console.log(products);
});
});
but it takes time and became problem if there related to another models.
I'm calling populte with this
Product.findOne({}).populate('_user').exec(function(err, p){
console.log(p);
});
but attribute _user was not set and undefined
any help?
Thanks

Mongoose Sorting

I have a problem with displaying data with sorting. Here is my query,
Activity.find({"user_id": sesUser._id}, {}, {offset: 0, limit : 5, sort:{createdAt:1}}, function (e,a){
...
});
I have data about 252 length, and my latest data is 9 June 2015. If i use this query i only get data from 5 June 2015/ last week data and not get the latest data, but if i not using sorting, the latest data is appear.
I have used this query below but turns out the result is same.
Activity.find({"user_id" : sesUser._id}).sort("createdAt").exec(function(err,a){
...
});
Any help? I'm using Mongoose v3
-- edited --
This is my Activity Schema / Model
var mongoose = require('mongoose');
var Activity = mongoose.Schema({
sender_id : {type: String, required: true },
user_id : {type: String, required: true },
product_id : {type: String, default : "" },
type : {type: String, required: true },
createdAt : {type: String, default : new Date()}
});
module.exports = mongoose.model('Activity', Activity);
`createdAt : {type: Date, default : new Date()}`
Type Date not string man
It will automatically create the createdAt and updatedAt
var options={
timestamps: true
}
var Activity = mongoose.Schema({
sender_id : {type: String, required: true },
user_id : {type: String, required: true },
product_id : {type: String, default : "" },
type : {type: String, required: true }
},options);

Resources