I am trying to write a schema that takes the parameters required by an activity. I am want to add a field 'activityParameters' that will be case specific depending on the activityType. Suppose if the activityType is email then the activityParameters should store details like'to:String, from:String, subject: String, body: String' and if the activity is "export" then it should store parameters like 'path:String' . Different types of activity will have different parameters. Please help me how to do this.
var activity_type = {
values: 'email export'.split(' '),
message: 'validation failed for path `{PATH}` with value `{VALUE}`'
};
var activitySchema = new Schema({
activityName: String,
activityDescription: String,
executionTime: {type: Date , default: null},
activityStartTime: {type: Date , default: null},
activityCompletionTime: {type: Date , default: null},
activityType: {type:String, enum: activity_type},
//activityParameters: ,
appName : String,
activityRetryCount: {type:Number,default:0},
createdOn: {type:Date , default:Date.now},
deletedOn: {type: Date , default: null},
updatedOn: {type: Date , default: null}
});
There's really no good answer for doing this with mongoose and maintaining a strongly typed schema.
You can specify all the fields for each type on the schema and then use them depending on type (export vs message).
var activitySchema = new Schema({
...
activityParameters: {
to:String,
from:String,
path:String
}});
You might consider having a key per subtype to be an improvement:
var activitySchema = new Schema({
...
emailActivityParameters:{
to:String,
from:String,
},
exportActivityParameters:{
path:String,
}
});
It would be pretty easy to access each "subdocument" depending on the activity type.
Finally, you can have a key called activityParameters and have that be freeform json:
var activitySchema = new Schema({
...
activityParameters: {}
});
In this case you can preserve your schema integrity using custom validators.
If these approaches don't appeal then maybe mongoose isn't the right tool you. You could use a lower level mongo driver and then something like Typescript or json schema to validate your models before you save them to mongoose. Check out this, for example: https://github.com/litixsoft/lx-valid.
Related
How Can we change the value of updated_at whenever Data of DB is updated
Consider this to be my Mongoose Schema,
const mongoose = require('mongoose')
const locationDataSchema = new mongoose.Schema({
locationName: String,
location: [{
lat: Number,
lng: Number
}],
news: [ {
author: String, //or number
title: String,
description: String,
url: String,
urlToImage: String
}],
updated_at: {
type: Date,
default: Date.now
}
})
From my Vaguely reading of Mongoose Docs, I did something like this
locationDataSchema.post('save', (next) => {
console.log("here")
this.locationName = this.locationName.toLowerCase();
this.updated_at = Date.now()
})
But this isn't being called whenever I create/update something in my mongoose Schema
Question: Can someone help me in figuring out how can I change
updated_at = Date.now()
Whenever user updates data in DB (similarly changing location Name to Lowercase)
The current version of Mongoose (v4.x) has time stamping as a built-in option to a schema:
var mySchema = new mongoose.Schema( {name: String}, {timestamps: true} );
This option adds createdAt and updatedAt properties that are timestamped with a Date, and which does all the work for you.
For more please look at
https://mongoosejs.com/docs/guide.html#timestamps
Mail Schema:
var mail = new mongoose.Schema({
globalId: {type: String, index: true, unique: true},
from: {type: String, required: true},
beginDate: {type: Date, required: true},
endDate: {type: Date},
source: {type: String},
address: {type: String},
subject: {type: String, required: true},
text: {type: String},
note: {type: String},
files: [
{
fileName: {type: String},
fileSize: {type: Number},
fileType: {type: String}
}
]
});
When I try to save new mail with endDate is null I have the following error
Mail validation failed: endDate: Cast to Date failed for value "null"
It looks like you are trying to save the schema with an invalid endDate.
If you do not want to save a date simply leave it out from the object. For instance all you need to provide for your schema at a minimum is:
const newMail = mail.create({
globalId: "1234",
from: "Jack",
beginDate: new Date(),
subject: "Random subject",
});
If you tried to do something like,
endDate: null
this will fail as it is expecting a Date() object or nothing at all.
As you have mentioned that you are storing endDate as null as String. That will definitely fail because your schema expecting a date and you are storing String.
Multiple ways you can approach:
Avoid endDate from JSON object. (You will not have endDate key in MongoDB)
set endDate: undefined that will automatically be ignored. (You will not have endDate key in MongoDB)
Set endDate: null not in String (You will have endDate key as null value)
Below one will not work:
var testObj = {
ct: "null", // null as String value
name: "Foo"
}
But this one will definitely work:
var testObj = {
ct: null, // null as value
name: "Foo"
}
Tested on below version:
MongoDB v3.2.18
Mongoose v4.10.8
Another point I want to add here is storing key: null will consume resources. If your requirement is not dependent on null values, I would like to use not to store a key in the database rather storing as null.
I have solved this by setting the default date to undefined. It will not show up on creation of your object. That is fine as it is now a frontend problem. If a key does not exist the frontend will default to undefined. If it remains undefined it will not be saved in your database. Avoid having a default null I have made that mistake too!
mail = {
endDate: {
type: Date,
default: undefined
}
}
In your schema you can mention the type as endDate : { type: Date || null} and wherever you feel like assign the value as null if date is not there OR you can even add default as null to schema.
So I've created the following model with a subschema (allocationAndLocationSchema). Each MovementSchema will have many embedded allocationAndLocationSchemas in it. I need to update individual allocationAndLocationSchemas and save them to the model.
I've tried doing a forEach on the subschema but updating that doesn't seem to work. Is there a simple solution to search for the model by id (createdByUserID) and the subschema by userID then update it's latitude and longitude?
Model and subschema:
var allocationAndLocationSchema = new Schema({
roleID: String,
roleTitle: String,
userID: String,
latitude: String,
longitude: String,
lastUpdated: Date
});
var MovementSchema = new Schema({
dateCreated: Date,
createdByUserID: String,
dateEdited: Date,
allocationAndLocation: [allocationAndLocationSchema]
});
Its just raw so, take idea from it.
MovementSchema.findByIdAndUpdate({createdByUserID:req.body.createdByUserID,'allocationAndLocation.userID': req.body.userID},{$set:{allocationAndLocation.$.longitude:req.body.longitude ,allocationAndLocation.$.latitude : req.body.latitude},function(err,res){
//call back function
}
Help! I'm losing my mind. I need to simply return a Mongo document, using Mongoose, IF a sub document does not exist.
My schemas:
var userSchema = new mongoose.Schema({
email: {type: String, unique: true, lowercase: true},
password: {type: String, select: false},
displayName: String,
picture: String,
facebook: String,
deactivation: deactiveSchema
});
var deactiveSchema = new mongoose.Schema({
when : { type: Date, default: Date.now, required: true },
who : { type: Schema.Types.ObjectId, required: true, ref: 'User' }
});
My goal is to lookup a user by their facebook ID if they have not been deactivated.
If they have been deactivated, then a deactivation subdocument will exist. Of course, to save space, if they are active then a deactivation will not exist.
On a side note, I'm also worried about how to properly construct the index on this logic.
I'd post snippets but every attempt has been wrong. =(
You can use $exists operator:
userSchema.find({deactivation:{$exists:false}}).exec(function(err,document){
});
or $ne:
userSchema.find({deactivation:{$ne:null}}).exec(function(err,document){
});
Since you are retiring data and not deleting, I'd go with one of two approaches:
Flag for retired (Recommended)
add to your schema:
retired: {
type: Boolean,
required: true
}
and add an index for this query:
userSchema.index({facebook: 1, retired: 1})
and query:
User.find({facebook: facebookId, retired: false}, callback)
Query for existence
User.find().exists("deactivation", false).exec(callback)
The latter will be slower, but if you really don't want to change anything, it will work. I'd recommend taking some time to read through the indexing section of the mongo docs.
Mongoose has many options for defining queries with conditions and a couple of styles for writing queries:
Condition object
var id = "somefacebookid";
var condition = {
facebook : id,
deactivation: { $exists : true}
};
user.findOne(condition, function (e, doc) {
// if not e, do something with doc
})
http://mongoosejs.com/docs/queries.html
Query builder
Alternatively, you may want to use the query builder syntax if you are looking for something closer to SQL. e.g.:
var id = "somefacebookid";
users
.find({ facebook : id }).
.where('deactivation').exists(false)
.limit(1)
.exec(callback);
I'm building a CRUD-style REST service with Node.js, Express and MongoDB using mongoose. This service is going to allow users of an already existing android application to upload/sync the contents of their individual databases online.
The data model for the already-existing application uses UUIDs (generated in Java) which clashes with the shorter, monotonic MongoDB style _id fields. Because the data model already exists and is populated with data from many users, I cannot convert the source data over to monotonic MongoDB-style _ids. This has left me with 2 options that I can think of: either 1) Make Mongo/Mongoose (or some other ODM) play nicely with full UUIDs instead of the monotonic _ids or 2) add a uuid field to the mongoose model in addition to the _id field and fight the pitfalls of this approach. I'm attempting to choose option #1 and running into issues with ObjectID references.
I originally stumbled upon mongoose-uuid, but unfortunately this isn't working for my use-case properly because it was overwriting my explicitly-set _id value when creating new Mongoose objects. Diving into the plugin code, it assumes that an object is new (by calling checking Mongoose's .isNew value) and thus overwrites the _id with a new uuid. Since I need to retain the original uuid when creating new documents in Mongo, this plugin isn't working for me.
Next, I found a post by Aaron Heckmann, creator of mongoose, on a similar topic. This has been helpful, however I am now encountering the problem where I cannot have my mongoose schemas reference each other by ObjectID, since they technically they are now referencing each other using String `_ids.
Schema example:
var mongoose = require('mongoose');
var uuid = require('node-uuid');
var Schema = mongoose.Schema;
var trackPassSchema = new Schema({
_id: { type: String, default: function genUUID() {
uuid.v1()
}},
//Omitting other fields in snippet for simplicity
vehicle: [
{type: Schema.Types.ObjectId, required: true, ref: 'Vehicle'}
]
});
module.exports = mongoose.model('TrackPass', trackPassSchema);
Referencing schema:
var mongoose = require('mongoose');
var uuid = require('node-uuid');
var Schema = mongoose.Schema;
var vehicleSchema = new Schema({
_id: { type: String, default: function genUUID() {
uuid.v1()
}},
//Omitting other fields in snippet for simplicity
description: {type: String},
year: {type: Number}
});
module.exports = mongoose.model('Vehicle', vehicleSchema);
When I attempt to call save() a trackPass that has been passed in from my application:
var trackPass = new TrackPass(req.body);
//Force the ID to match what was put into the request
trackPass._id = req.params.id;
trackPass.save(function (err) { ... }
I get the following error:
{ [CastError: Cast to ObjectId failed for value "b205ac4d-fd96-4b1e-892a-d4fab818ea2a" at path "vehicle"]
message: 'Cast to ObjectId failed for value "b205ac4d-fd96-4b1e-892a-d4fab818ea2a" at path "vehicle"',
name: 'CastError',
type: 'ObjectId',
value: ["b205ac4d-fd96-4b1e-892a-d4fab818ea2a"],
path: 'vehicle' }
I believe this error makes sense as I'm now using Strings which are longer than typical Mongo ObjectIDs. Without having the ObjectID reference, I don't believe I will be able to populate() referenced objects from other collections. I suppose I could simply not reference the other nested objects in my schema definitions, however I don't like this approach as I feel I will be losing a lot of the benefit of utilizing the ODM. Any other thoughts?
You can still use populate() with _id values of types besides ObjectID, but you do need to use the same type in the reference definition.
So your trackPassSchema would need to change to:
var trackPassSchema = new Schema({
_id: { type: String, default: function genUUID() {
return uuid.v1()
}},
vehicle: [
{type: String, required: true, ref: 'Vehicle'}
]
});
As Adam notes in the comments, you could simplify your default value to:
var trackPassSchema = new Schema({
_id: { type: String, default: uuid.v1 },
vehicle: [
{type: String, required: true, ref: 'Vehicle'}
]
});
Both JohnnyHK and Adam C answers are correct. But if you're using uuid in schema for an array of objects, it is good to use it like this
var trackPassSchema = new Schema({
_id: { type: String, default: () => uuid.v1 },
vehicle: [
{type: String, required: true, ref: 'Vehicle'}
]
});
Because, in one such scenario when i tried using like this _id: { type: String, default: () => uuid.v1 } multiple objects of the array had the same id.
It is not possible in this case as _id is unique field, but it can happen when you are using with fields that aren't unique.