Saving an array in Mongoose save - mongodb

Need help with arrays in Mongoose. Here is how my schema looks :-
const alertsSchema = new mongoose.Schema({
Alert_Date: String,
Alert_StartTime: String,
Alert_EndTime: String,
Alert_RuleName: String,
Alert_RuleId: String,
Alert_EntryNumber: String,
Alert_AlertId: String,
Alert_Description: String,
Alert_TriggerTime: String,
Alert_Activities: [{
Alert_Activities_ActivityType: String,
Alert_Activities_ActivityTime: String,
Alert_Activities_AreaName: String,
Alert_Activities_AreaType: String,
Alert_Activities_Position: Array,
Alert_Activities_Duration: Number,
Alert_Activities_SecondVesselId: String,
Alert_Activities_SecondVesselName: String,
Alert_Activities_SecondVesselClassCalc: String,
Alert_Activities_SecondVesselSize: String,
Alert_Activities_SecondVesselMMSI: String,
Alert_Activities_SecondVesselIMO: String,
}],
})
The Alert_Activities is an array coming from my upstream node js application. I implemented a fswatch functionality and as soon as a particular file changes, I am looking to save the record in my collection. The upstream file will always contain an array. Generally on an average of around 4 to 5 records. In short Alert_Activities will be there for every element of the array.
I am running a for loop and then trying to save all four elements in one go into my collection.myObject is the full array read from the upstream file using fs.read
for(i=0; i<myObject.length; i++){
var newAlertData = new alertRegister({
Alert_Date: date1,
Alert_StartTime: startNotificationDate,
Alert_EndTime: endNotificationDate,
Alert_RuleName: myObject[i].ruleName,
Alert_RuleId: myObject[i].ruleId,
Alert_EntryNumber: myObject[i].entryNumber,
Alert_AlertId: myObject[i].alertId,
Alert_Description: myObject[i].description,
Alert_TriggerTime: myObject[i].triggerTime,
// Alert_Activities: myObject[i].activities,
});
newAlertData.save(function(err,data1){
if(err){
console.log(err)
} else {
console.log("data saved")
}
})
The Alert_Activities will obviously not save. What is the right way to do this in Mongoose?

If you are dealing with Mongoose Documents, not with .lean() javascript objects, probably, you'll need to mark array field(s) as modified via markModified and only then use .save().
Also, directModifiedPaths could help you to check, what fields has been modified in the document.
I have noticed that your Alert_Activities, is actually an array of objects, so make sure that the result mongoose documents, which you are trying to .save() really satisfy all the validation rules.
If changes on some fields do successfully saved but the others - don't, then if's definitely something wrong with field names, or validation. And the DB/mongoose doesn't trigger you an error, because the document has already been saved, even in particular.

Related

Whats the difference between having an array of objects and array of ObjectIds on mongoDB?

Suppose I have the following schema:
const personSchema = mongoose.Schema({
firstname: String,
lastname: String,
email: String,
gender: {type: String, enum: ["Male", "Female"]}
dob: Date,
city: String,
interests: [interestsSchema],
// vs this
// interests: [{type: Schema.Types.ObjectId(), ref: 'Interest'}]
});
What are the difference between the two methods here? What are the advantages and disadvantages of one vs the other?
There is obviously no perfect answer to this question, it all depends on the scenario, where and how will you use the data.
ObjectId vs Embedded
I use both embedded documents and lists of ObjectIds. For example, I have two collections, Customers and Contacts. Every Customer can have multiple contacts, and multiple Customers should be able to use the same contact.
We also want to list and manage contacts separately.
We also have an array of addresses, which is unique to each Customer document, we do not need to list or manage them separately or reuse them ever again, so it makes sense to embed them.
Customer
{
businessName: string,
contacts: [ObjectId, ObjectId],
address: {
invoice: [{
street: string
zip: string
city: string
}]
}
}
Contact
{
firstName: string
lastName: string
phoneNumber: string
email: string
}
Array size
This is just a side note, not relevant to the question, just some more information about arrays in MongoDB.
What you are describing here is a so called one-to-many relationship, where one document may have any number of embedded documents.
MongoDB works best with "one-to-few" when using any type of array, they usually recommend you to not have an array containing more than a hundred documents, if there are more, it will start to impact query times in one way or another.
The same goes for the ObjectId array, it should not be more than a hundred in the array.
If you expect the array to increase beyond a hundred, you should use a separate collection with a property like personId where every interest refers to the person. This way, you don't need the array at all in the personSchema.

Mongoose: Delete Item - First One always gets deleted

I am completely new to the fields of Mongoose and MongoDB.
I am currently trying trying to remove one element from my database.
This is my code so far:
My issueModel:
var mongoose = require('mongoose'); // loading module for mongoose
var db = mongoose.connect('mongodb://localhost/issuedb');
var issueSchema = new mongoose.Schema({
title: String,
description: String,
priority: String,
status: String
});
// Constructor Function:
var issueModel = mongoose.model('issues', issueSchema); // have to give the
name of the collection where the element should be stored + Schema
// Export this Construction Function for this Module:
module.exports = issueModel; // careful: module != model !
My post method for using the delete method:
// creating the router for deleting one item:
router.post('/delete/:id', (req, res) => {
console.log(req.params.id);
issueModel.remove({id: req.params.ObjectId})
.setOptions({ single: true }).exec(function (err, deleted) {})
.then(issues => res.render('issue', {issues: issues}));
The thing i would like to do here is using the object id - which is correctly stored in req.params.ObjectID according to my console.log, and deleting the corresponding object.
But currently , when i have got a table with about 3-4 entries, always the first one gets deleted. Why is that? I am really TOTALLY new and really tried searching a lot, but i could not find any solution until now. I am happy about any tips that would help me.
What am i doing wrong?
The ID in the URL and the Object.ID are the same! Why is the first object deleted then, not the second or the third?
I am hopeless right now.
I also read about the remove() option not being really used in todays time. But we were told at university to use this method right now.
I also tried findOneByID and delete methods i found in the mongoose database.
If you need any more code please let me know!
You can use one of the convenience methods for this: findByIdAndRemove:
issueModel.findByIdAndRemove(req.params.ObjectId, function(err) {
if (err) { ... failed }
});
This will remove a whole document matching the ID which I think its what you want, if you want to a remove property from a document that's a different query.
If you don't use one of the convenience methods which just take IDs (have ById in them), then you have to convert your ID from a string to an ObjectId:
const { ObjectId } = require('mongodb');
issueModel.remove({ id: ObjectId(req.params.ObjectId) }).setOptions({ single: true })

How create a Date field with default value as the current timestamp in MongoDb?

How to create a date field with default value,the default value should be current timestamps whenever the insertion happened in the collection.
Thats pretty simple!
When you're using Mongoose for example, you can pass functions as a default value.
Mongoose then calls the function for every insertion.
So in your Schema you would do something like:
{
timestamp: { type: Date, default: Date.now},
...
}
Remember to only pass the function object itself Date.now and not the value of the function call Date.now()as this will only set the Date once to the value of when your Schema got created.
This solution applies to Mongoose & Node.Js and I hope that is your usecase because you did not specify that more precisely.
Use _id to get the timestamp.
For this particular purpose you don't really need to create an explicit field for saving timestamps. The object id i.e. "_id", that mongo creates by default can be used to serve the purpose thus, saving you an additional redundant space. I'm assuming that you are using node.js so you can do something like the following to get the time of particular document creation:
let ObjectId = require('mongodb').ObjectID
let docObjID = new ObjectId(<Your document _id>)
console.log(docObjID.getTimestamp())
And, if you are using something like mongoose, do it like this:
let mongoose = require('mongoose')
let docObjID = mongoose.Types.ObjectId(<Your document _id>)
console.log(docObjID.getTimestamp())
Read more about "_id" here.
When Creating Document, timestamps is one of few configurable options which can be passed to the constructor or set directly.
const exampleSchema = new Schema({...}, { timestamps: true });
After that, mongoose assigns createdAt and updatedAt fields to your schema, the type assigned is Date.
You would simply do this while inserting... for current timestamp.
collection.insert({ "date": datetime.now() }
Let's consider the user schema in which we are using created date, we can use the mongoose schema and pass the default value as Date.now
var UserSchema = new Schema({
name: {type: String, trim: true},
created: {type: Date, default: Date.now}
});
If we want to save timetamp instead of number then use Number isntead of number like that
var UserSchema = new Schema({
name: {type: String, trim: true},
created: {type: Number, default: Date.now}
});
Note:- When we use Date.now() in the default parameter then this will
only set the Date once to the value of when your Schema got created,
so you'll find the dates same as the that in the other document. It's better to use Date.now instead of Date.now().
Here's a command that doesn't set a default, but it inserts an object with the current timestamp:
db.foo.insert({date: new ISODate()});
These have the same effect:
db.foo.insert({date: ISODate()});
db.foo.insert({date: new Date()});
Be aware that Date() without new would be different - it doesn't return an ISODate object, but a string.
Also, these use the client's time, not the server's time, which may be different (since the time setting is never 100% precise).
I just wish to point out that in case you want the timestamp to be stored in the form of an integer instead of a date format, you can do this:
{
timestamp: { type: Number, default: Date.now},
...
}
Thanks friends ..
I found another way to get timestamp from _id field. objectid.gettimestamp() from this we can get it time stamp.
This is a little old, however I fount when using the Date.now() method, it doesn't get the current date and time, it gets stuck on the time that you started your node process running. Therefore all timestamps will be defaulted to the Date.now() of when you started your server.
One way I worked around this was to do the following:
ExampleSchema.pre('save', function (next) {
const instanceOfSchema = this;
if(!instanceOfSchema.created_at){
instanceOfSchema.created_at = Date.now();
}
instanceOfSchema.updated_at = Date.now();
next();
})
createdAt: {type: Date, default:Date.now},

Mongoose - update after populate (Cast Exception)

I am not able to update my mongoose schema because of a CastERror, which makes sence, but I dont know how to solve it.
Trip Schema:
var TripSchema = new Schema({
name: String,
_users: [{type: Schema.Types.ObjectId, ref: 'User'}]
});
User Schema:
var UserSchema = new Schema({
name: String,
email: String,
});
in my html page i render a trip with the possibility to add new users to this trip, I retrieve the data by calling the findById method on the Schema:
exports.readById = function (request, result) {
Trip.findById(request.params.tripId).populate('_users').exec(function (error, trip) {
if (error) {
console.log('error getting trips');
} else {
console.log('found single trip: ' + trip);
result.json(trip);
}
})
};
this works find. In my ui i can add new users to the trip, here is the code:
var user = new UserService();
user.email = $scope.newMail;
user.$save(function(response){
trip._users.push(user._id);
trip.$update(function (response) {
console.log('OK - user ' + user.email + ' was linked to trip ' + trip.name);
// call for the updated document in database
this.readOne();
})
};
The Problem is that when I update my Schema the existing users in trip are populated, means stored as objects not id on the trip, the new user is stored as ObjectId in trip.
How can I make sure the populated users go back to ObjectId before I update? otherwise the update will fail with a CastError.
see here for error
I've been searching around for a graceful way to handle this without finding a satisfactory solution, or at least one I feel confident is what the mongoosejs folks had in mind when using populate. Nonetheless, here's the route I took:
First, I tried to separate adding to the list from saving. So in your example, move trip._users.push(user._id); out of the $save function. I put actions like this on the client side of things, since I want the UI to show the changes before I persist them.
Second, when adding the user, I kept working with the populated model -- that is, I don't push(user._id) but instead add the full user: push(user). This keeps the _users list consistent, since the ids of other users have already been replaced with their corresponding objects during population.
So now you should be working with a consistent list of populated users. In the server code, just before calling $update, I replace trip._users with a list of ObjectIds. In other words, "un-populate" _users:
user_ids = []
for (var i in trip._users){
/* it might be a good idea to do more validation here if you like, to make
* sure you don't have any naked userIds in this array already, as you would
*/in your original code.
user_ids.push(trip._users[i]._id);
}
trip._users = user_ids;
trip.$update(....
As I read through your example code again, it looks like the user you are adding to the trip might be a new user? I'm not sure if that's just a relic of your simplification for question purposes, but if not, you'll need to save the user first so mongo can assign an ObjectId before you can save the trip.
I have written an function which accepts an array, and in callback returns with an array of ObjectId. To do it asynchronously in NodeJS, I am using async.js. The function is like:
let converter = function(array, callback) {
let idArray;
async.each(array, function(item, itemCallback) {
idArray.push(item._id);
itemCallback();
}, function(err) {
callback(idArray);
})
};
This works totally fine with me, and I hope should work with you as well

Does Mongoose only support embedded documents in arrays?

I have some data in MongoDB that looks like this:
{
name: "Steve",
location: {
city: "Nowhere, IL",
country: "The United States of Awesome"
}
}
I’m using objects to organize common data structures (like locations), which in Mongoose might map nicely to Schemas. Unfortunately, they don't appear to really work in Mongoose.
If I just embed an object, like this:
{
name: String,
location: {
city: String,
country: String
}
}
It appears to work, but exhibits some bizarre behavior that causes problems for me (e.g. instance.location.location returns location, and subobjects inherit methods from the parent schema). I started a thread on the Mongoose list, but it hasn’t seen any action.
If I embed a Schema, like this:
{
name: String,
location: new Schema({
city: String,
country: String
})
}
…my application doesn’t start (Schema isn’t a type supported by Mongoose). Ditto for
{
name: String,
location: Object
}
…which wouldn’t be ideal, anyway.
Am I missing something or do my schemas not jive with Mongoose?
I did something similar:
var Topic = new Schema({
author : ObjectId
, title : String
, body : String
, topics : [Topic]
});
This worked fine in my tests. However, removing the array brakets resulted in an error. Looks like a bug to me.
https://github.com/LearnBoost/mongoose/blob/master/lib/mongoose/schema.js#L185
Dumping types, I only get String, Number, Boolean, DocumentArray, Array, Date, ObjectId, Mixed -- which appears to be on purpose, schema/index.js doesn't look like it dynamically registers new Schemas to the list of types, so I am guessing this isn't a supported use case, yet.
https://github.com/LearnBoost/mongoose/issues/188
"Embedding single docs is out of the question. It's not a good idea (just use regular nested objects)"
Josh
It looks like this was a bug, it’s been fixed in Mongoose 2.0!