How to send ISODate in meteor.call method - mongodb

I am creating an array of objects in client side in the meteor, and each object have modified date inside it as mentioned below:
Client Side:
student;
here student is an array of an object contains name, id, roll_no
var array = [];
student.forEach(function(singleStud, index){
var single_obj ={
"name":singleStud.name,
"student_id":singleStud.id,
"roll_no":singleStud.roll_no,
"college_name":"ABC college",
"college_id":"xyz Id",
"created_date": new Date()
}
array.push(single_obj);
},this)
Meteor.call('saveDetails', array, function (error, result) {
console.log("data Saved Successfully");
});
Server Side:
I have used plugin mikowals:batch-insert to insert an array which is equivalent to insertMany in mongo.
Meteor.methods({
"saveDetails": function (array) {
try {
studentDetails.batchInsert(array);
return true;
} catch (err) {
return err;
}
}
});
when I save it the created_date is saving it as a string ("2018-04-23T10:26:26.766Z"), but I want it to be saved it as a date data type (ISODate("2018-04-23T10:26:26.766Z")).
How can I achieve from the client side in meteor.call ???

This is actually a bug in mikowals:batch-insert. mikowals-batch-insert recursively attempts to convert objects into a JSON format friendly with MongoDB. As part of this process, it uses underscore clone to make shallow copies of primitive types. While Date is a primitive, it cannot be cloned with _.clone, so it mutates your date to a string (eww). You should open an issue with mikowals:batch-insert.
Regardless, you shouldn't be defining this data on the client. The client could maliciously inject false information (which could break your application's logic). Instead, you should map over the input and inject the date into incoming objects.

Related

Why i can't replace field in mongoose document?

vk_account.settings is an Array in mongoose schema.
vk_account.save(function(err, vk_account) {
if(err) return cb(err, null);
vk_account.Sources = updated_sources;
vk_account.settings = account_setting;
cb(null, vk_account);
})
I want to replace vk_account.settings(Array) to one certain object from the vk_account.settings, which is account_setting(Object), but when I replace it, it does not get replaced and vk_account.settings as before has old value.
As I understand it, the mongoose prohibits and does not assign an Object to a variable of type Array, but I don't want to save it, I just want to send it to the client and forget.
Firstly you cannot assign same value for save as well as for response vk_account... Then you have to change the instance to object using toObject() and then you can add keys in it
vk_account.save(function(err, vk) {
if(err) return cb(err, null);
var object = vk.toObject();
vk.Sources = updated_sources;
vk.settings = account_setting;
cb(null, vk);
})

how to set a trigger to add to a date in meteor using quickform

I have MemberProfiles and MemberPayments collections.The MemberProfile has expiryDate field which is set to current date at insert.I need to extend expirDate of a unique MemberProfile whenever a MemberPayment is added to that MemberProfile.
MemberProfiles = new Mongo.Collection('memberProfiles');
MemberProfileSchema = new SimpleSchema({
expiryDate: {
type: Date,
autoValue: function () {
return moment().toDate();
},
autoform: {
type: "hidden"
}
}
// to insert into Memb erProfiles
{{> quickForm collection="MemberProfiles" id="insertMemberProfileForm" type="insert" class="new-recipe-form"}}
//the code for MemberPayments collection
MemberPayments = new Mongo.Collection('memberPayments');
MemberPayments.before.insert(function (userId, doc) {
let memberProfile= MemberProfiles.direct.findOne({profile: doc.memberId});
MemberProfiles.update(doc.memberId, {
$set: {
expiryDate: moment().add(31, 'days');
,
}
}
)
});
I have added all the necessary packages but still this doesnt work.I am getting error Cannot set property 'expiryDate' of undefined
It is challenging to try and resolve issues like this without having a more complete example of the app or reference to the complete project in github or somewhere else.
However, when I read through your code I noticed an issue in your MemberProfiles.update() function. I also noticed that it appears you are only processing your form from the client side (e.g. because your quickform is not using a Meteor Method) so you will have to manually call the SimpleSchema .clean() method to generate your autovalue. Keep in mind that your client side approach might work ok now, but once you remove the insecure package you will either have to implement a Meteor Method to perform the insert or configure your collection allow/deny rules to allow client side insert (this is dangerous).
Since you are using moment.js you need to be careful that you always pull the date from the moment object before storing in mongodb. In this case, you are trying to set expiryDate to the value returned from moment().add(31, 'days') which is just another moment object.
Also, I would assume you want to add 31 days to the current value of expiryDate, however you are never initializing moment with the expiryDate. Therefore, you will always be setting the expiryDate to 31 days from the time the function executes.
Lastly, you have a syntax error (; inside your $set object) and your findOne selector includes {profile: doc.memberId} however your MemberProfiles schema says there is only a _id and expiryDate field in your collection.
Try this new logic that addresses the above issues and see if that resolves your issue.
MemberPayments.before.insert(function (userId, doc) {
let memberProfile = MemberProfiles.direct.findOne({profile: doc.memberId});
if (memberProfile) {
if (!memberProfile.expiryDate) {
console.log("expiryDate was not previously set!");
} else {
MemberProfiles.update({profile: doc.memberId}, {
$set: {
expiryDate: moment(memberProfile.expiryDate).add(31, 'days').toDate()
}
});
}
} else {
console.log("memberProfile not found");
}
});
Now that this is fixed, you need to resolve the issue of your autovalue not being generated on the client side. You do this by calling the SimpleSchema .clean() method. Since you are not using Meteor Methods to process your quickForm (and therefore doing everything client side), you need to add the below AutoForm hook to ensure that the SimpleSchema .clean() method is called before the doc is saved (which will then execute your autovalue logic).
AutoForm.hooks({
insertMemberProfileForm: {
before: {
insert: function(doc) {
MemberProfileSchema.simpleSchema().clean(doc);
return doc;
}
}
}
});
You should put the above code in the onRendered() callback of the template that creates your quickform (e.g. the template that contains the below code in the HTML).
{{> quickForm collection="MemberProfiles" id="insertMemberProfileForm" type="insert" class="new-recipe-form"}}

Query sailsjs blueprint endpoints by id array using request

I'm using the request library to make calls from one sails app to another one which exposes the default blueprint endpoints. It works fine when I query by non-id fields, but I need to run some queries by passing id arrays. The problem is that the moment you provide an id, only the first id is considered, effectively not allowing this kind of query.
Is there a way to get around this? I could switch over to another attribute if all else fails but I need to know if there is a proper way around this.
Here's how I'm querying:
var idArr = [];//array of ids
var queryParams = { id: idArr };
var options: {
//headers, method and url here
json: queryParams
};
request(options, function(err, response, body){
if (err) return next(err);
return next(null, body);
});
Thanks in advance.
Sails blueprint APIs allow you to use the same waterline query langauge that you would otherwise use in code.
You can directly pass the array of id's in the get call to receive the objects as follows
GET /city?where={"id":[1, 2]}
Refer here for more.
Have fun!
Alright, I switched to a hacky solution to get moving.
For all models that needed querying by id arrays, I added a secondary attribute to the model. Let's call it code. Then, in afterCreate(), I updated code and set it equal to the id. This incurs an additional database call, but it's fine since it's called just once - when the object is created.
Here's the code.
module.exports = {
attributes: {
code: {
type: 'string'//the secondary attribute
},
// other attributes
},
afterCreate: function (newObj, next) {
Model.update({ id: newObj.id }, { code: newObj.id }, next);
}
}
Note that newObj isn't a Model object as even I was led to believe. So we cannot simply update its code and call newObj.save().
After this, in the queries having id arrays, substituting id with code makes them work as expected!

getting values from WriteResult mongo

I was trying to get familiar with the WriteResult object in mongo, but I can't access any of its values. The docs say the number of values inserted is stored in WriteResult.nInserted. Trying to access nInserted is crashing my server.
var readings = new Readings({
val1: parseInt(Data[0]),
val2: parseInt(Data[1]),
val3: parseInt(Data[2]),
val4: parseInt(Data[3]),
val5: parseInt(Data[4]),
val6: parseInt(Data[5]),
})
var result = readings.save(function (err, post){
if(err){return next(err)}
res.status(201).json(readings)
})
if(result.nInserted > 0){
console.log('wrote to database')
}
else{
console.log('could not write to database')
}
I know the data is being written to the database. I see it in the mongo shell.
The save method on a model instance doesn't return anything. All results are reported via the callback method, so you'd use something like this:
readings.save(function (err, doc, numberAffected){
if(err){return next(err)}
if (numberAffected > 0) {
console.log('updated an existing doc');
} else {
console.log('added a new doc');
}
res.status(201).json(doc)
})
Mongoose doesn't give you access to the full WriteResult, but as long as err is null you can rest assured the save succeeded and it's only a matter of whether an existing doc was updated or a new one was added. Because you're creating a new doc here, numberAffected will always be 0.

Subscribing to Meteor.Users Collection

// in server.js
Meteor.publish("directory", function () {
return Meteor.users.find({}, {fields: {emails: 1, profile: 1}});
});
// in client.js
Meteor.subscribe("directory");
I want to now get the directory listings queried from the client like directory.findOne() from the browser's console. //Testing purposes
Doing directory=Meteor.subscribe('directory')/directory=Meteor.Collection('directory') and performing directory.findOne() doesn't work but when I do directory=new Meteor.Collection('directory') it works and returns undefined and I bet it CREATES a mongo collection on the server which I don't like because USER collection already exists and it points to a new Collection rather than the USER collection.
NOTE: I don't wanna mess with how Meteor.users collection handles its function... I just want to retrieve some specific data from it using a different handle that will only return the specified fields and not to override its default function...
Ex:
Meteor.users.findOne() // will return the currentLoggedIn users data
directory.findOne() // will return different fields taken from Meteor.users collection.
If you want this setup to work, you need to do the following:
Meteor.publish('thisNameDoesNotMatter', function () {
var self = this;
var handle = Meteor.users.find({}, {
fields: {emails: 1, profile: 1}
}).observeChanges({
added: function (id, fields) {
self.added('thisNameMatters', id, fields);
},
changed: function (id, fields) {
self.changed('thisNameMatters', id, fields);
},
removed: function (id) {
self.removed('thisNameMatters', id);
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
});
No on the client side you need to define a client-side-only collection:
directories = new Meteor.Collection('thisNameMatters');
and subscribe to the corresponding data set:
Meteor.subscribe('thisNameDoesNotMatter');
This should work now. Let me know if you think this explanation is not clear enough.
EDIT
Here, the self.added/changed/removed methods act more or less as an event dispatcher. Briefly speaking they give instructions to every client who called
Meteor.subscribe('thisNameDoesNotMatter');
about the updates that should be applied on the client's collection named thisNameMatters assuming that this collection exists. The name - passed as the first parameter - can be chosen almost arbitrarily, but if there's no corresponding collection on the client side all the updates will be ignored. Note that this collection can be client-side-only, so it does not necessarily have to correspond to a "real" collection in your database.
Returning a cursor from your publish method it's only a shortcut for the above code, with the only difference that the name of an actual collection is used instead of our theNameMatters. This mechanism actually allows you to create as many "mirrors" of your datasets as you wish. In some situations this might be quite useful. The only problem is that these "collections" will be read-only (which totally make sense BTW) because if they're not defined on the server the corresponding `insert/update/remove' methods do not exist.
The collection is called Meteor.users and there is no need to declare a new one on neither the server nor the client.
Your publish/subscribe code is correct:
// in server.js
Meteor.publish("directory", function () {
return Meteor.users.find({}, {fields: {emails: 1, profile: 1}});
});
// in client.js
Meteor.subscribe("directory");
To access documents in the users collection that have been published by the server you need to do something like this:
var usersArray = Meteor.users.find().fetch();
or
var oneUser = Meteor.users.findOne();