Mongoose Schema extend timestamp to have new properties (username) - javascript

I´m using MongoDB and mongoose for a project that needs to track data creation and changes. The requirements are that I need to keep track of records creation and changes including the date/time and the application user (not the OS user) who did it.
I´ve seen the mongoose timestamps option that would solve my date/time issue, but is there a way to extend it to include extra properties, where I´m gonna be adding the application username ?
If not, is there a single place where I can write a function that will be called on every creation/update so that I can include/modify these fields ?
Today I´m insering these properties on every model like below, but I would like to move all of them to a single place.
var companySchema = mongoose.Schema({
name: {
type: String,
index: true
},
phone: {
type: String
},
deleted: {
type: Boolean
},
createdAt: {
type: Date
},
createdBy: {
type: String
},
updatedAt: {
type: Date
},
updatedBy: {
type: String
}
});
How would be the best approach for it ?

I would approach it by creating two models, one for each Data created, one for each Data Changes.
Data created which will have 6 fields one is createdBy, createdAt, and one will be a field with an array of reference id of Data Changes, deletedBy, deletedAt, deletedFlag.
Data Changes will have fields dataID which will have reference id of data created, updatedBy and updatedAt.
This way it will be easy to keep track of when it was created when it was changes and when it was deleted.
PS: You can remove either of Array in Data created model or dataID ref id in Data Change mode, it was just me being extra cautious while binding.

Related

MongoDB move ObjectId from one field to another

Problem
I have an ObjectId saved in a field -incomingFriendRequests- and I want to move it to another field -friends-.
Is there a better way to do this than to remove it from receivedFriendRequests and then do an findOneAndUpdate to add it to friends? I feel like this can be accomplished with one database call instead of two.
Code
Mongoose Model:
const userSchema = mongoose.Schema({
friends: [{
type: mongoose.Schema.Types.ObjectId, ref: 'User', require: false
}],
incomingFriendRequests:[{
type: mongoose.Schema.Types.ObjectId, ref: 'User', require: false
}],
online: {type: Boolean, require: true}
});
It is not possible in a single query you need to first perform $pull on the incomingFriendRequests field for remove selected objectId and then you need to perform another update query on friends for add selected objectId. also, you can use $addToSet to preventing duplication id. if you use $push for add in that case duplicate objectId also store.
Query :-
1) Remove :- db.getCollection('testdata').update({id:ObjectId('5cc3fa66a04275096417f9ca')},{"$pull":{"incomingFriendRequests":"5cc3fa66a04275096417f9da"}}});
2) add :-
db.getCollection('testdata').update({id:ObjectId('5cc3fa66a04275096417f9ca')},{"$addToSet":{"friends":"5cc3fa66a04275096417f9da"}}); // also you can use $push
Moving values from one field to another would definitely need n + 1 calls to do the transformation. This can be achieved using
find({}).forEach(function(doc) {
// your doc update
doc.save();
});
Or you can even perform a bulkwrite.
In case if your goal is to delete the column incomingFriendRequests, you can probably rename the column to friends instead copying all the data.
update({}, { $rename: {incomingFriendRequests: friends }});

Mongoose create document in wrong key:value order according to schema

So I have a problem with understanding of how Mongo .create and .findAndUpdate operation works. I have mongoose 5.4.2X and a model with schema which has lot of key:value pairs (without any nested objects) in the exact order (in the code below I use 1. 2. 3. etc to show you the right order) like this:
let schema = new mongoose.Schema({
1.code: {
type: String,
required: true
},
2.realm: {
type: String,
required: true,
},
3.type: {
type: String,
required: true,
enum: ['D', 'M'],
},
4.open: Number,
5.open_size: Number,
6.key: typeof value,..
7...another one key: value like previous one,
8.VaR_size: Number,
9.date: {
type: Date,
default: Date.now,
required: true
}
});
and a class object which have absolutely the same properties in the same order like schema above.
When I form data for Mongo via const contract = new Class_name (data) and using console.log(contract) I have a necessary object with properties in the exact right order like:
Contract {1.code: XXX, 2.realm: YYY, 3.type: D, .... 8.VaR_size: 12, 9.date: 327438}
but when I'm trying to create/update document to the DB via findOneAndUpdate or (findByID) it writes in alphabetical order but not the necessary 1->9, for example:
_id:5ca3ed3f4a547d4e88ee55ec
1.code:"RVBD-02.J"
7.VaR:0.9
(not the 1->9)...:...
8.VaR_size:0.22
__v:0
5.avg:169921
The full code snippet for writing is:
let test = await contracts.findOneAndUpdate(
{
code: `${item_ticker}-${moment().subtract(1, 'day').format('DD.MMM')}` //how to find
},
contract, //document for writinng and options below
{
upsert : true,
new: true,
setDefaultsOnInsert: true,
runValidators: true,
lean: true
}
).exec();
Yes, I have read the mongoose docs and don't find any option param for
solving my problem or probably some optional params are matter but
there are no description for this.
It's not my first project, the funny thing is, that when I'm inserting
tons-of-docs via .insertMany docs are inserted according to schema
order (not alphabetical)
My only question is:
How do I fix it? Is there any option param or it's a part of findAnd....
operations? If there is not solution, what should I do if for me it's
necessary the right ordering and check existence of document before
inserting it?
Update #1 after some time I rephrase my search query for google and find a relevant question on SW here: MongoDB field order and document position change after update
Guess I found the right answer according to the link that I post earlier. So yes, it's part of MongoDB:
MongoDB allocates space for a new document based on a certain padding
factor. If your update increases the size of the document beyond the
size originally allocated the document will be moved to the end of the
collection. The same concept applies to fields in a document.
by #Bernie Hackett
But in this useful comment still no solution, right? Wrong. It seems that the only way to evade this situation is using additions optional params during Model.find stage. The same ordering using during project stage via .aggregate and it looks like this:
Model.find({query},{
"field_one":1,
"field_two":1,
.............
"field_last":1
});

How to add a collection2 schema to users collection using accounts-password package in meteorjs?

So, I have just started a meteor project and have included the accounts-password package. The package only supports few keys. I want to add a new SimpleSchema to the users collection with some more fields.
I am not given to create another instance of users collection with
#users = Mongo.Collection('users');
//Error: A method named '/users/insert' is already defined
I can attach a schema but will be forced to keep alot of fields optional, and may not be able to register with the default package otherwise.
Can I add simpleSchema without making other fields optional and still be able to login properly?
Or is there any other work around for this case?
Thank you for help in advance
You can get users collection with:
#users = Meteor.users;
You can find nice example of defining user collection in the docs of collection2 package: https://atmospherejs.com/aldeed/collection2
Schema = {};
Schema.User = new SimpleSchema({
username: {
type: String,
regEx: /^[a-z0-9A-Z_]{3,15}$/
},
emails: {
type: [Object],
// this must be optional if you also use other login services like facebook,
// but if you use only accounts-password, then it can be required
optional: true
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email
},
"emails.$.verified": {
type: Boolean
},
createdAt: {
type: Date
},
profile: {
type: Schema.UserProfile,
optional: true
},
services: {
type: Object,
optional: true,
blackbox: true
},
// Add `roles` to your schema if you use the meteor-roles package.
// Option 1: Object type
// If you specify that type as Object, you must also specify the
// `Roles.GLOBAL_GROUP` group whenever you add a user to a role.
// Example:
// Roles.addUsersToRoles(userId, ["admin"], Roles.GLOBAL_GROUP);
// You can't mix and match adding with and without a group since
// you will fail validation in some cases.
roles: {
type: Object,
optional: true,
blackbox: true
},
// Option 2: [String] type
// If you are sure you will never need to use role groups, then
// you can specify [String] as the type
roles: {
type: [String],
optional: true
}
});
You have three ways to adapt for the attach of a schema to such a collection:
Make every new field optional.
Have default values (friends defaults to [] for example).
Update the UI to include new mandatory elements (a radio for "P = NP" or "P != NP").
Each option is somewhat valid in itself. Chose what seems the most logical in the current context and what will give you the least headaches.
Do you absolutely need a user-given value for someField when he registers? Then you have to update the UI to fetch this value.
Is the presence of someField important, and it can be initialized to a default object (an empty array, null, 0...)? Then a default value will fit, and it will be added when Collection2 cleans the document.
None of the above? Optional.
As a somewhat personal note, I prefer this kind of code:
someUser.friends.forEach(sendGifts);
To this kind:
if(someUser.hasOwnProperty('friends')) {//Or _.has(someUser, 'friends') but it sounds sad
someUser.friends.forEach(sendGifts);
}
In the second code friends is an optional field, so we're not sure if it's present or undefined. Calling forEach on undefined results in a nice big error, so we have to check for the field existence first... Thus, I would advise slightly avoiding optional fields for consistency and simplicity.

Manipulating field content on client / server using autoform for meteor

I am playing around with Meteor and autoform.
My setup
I have an input field (type "time") as part of my schema for the collection of "Songs" that forces the user to type the syntax [num][num]:[num][num].
This is my schema:
time: {
type: Number,
label: "Time",
optional: true,
autoform: {
afFieldInput: {
type: 'time'
}
}
}
What I want to do
After hitting "submit" but before validation I want to convert the string (e.g. "03:45") to seconds (Number) so that the validation passes without an error.
Also: when reading the data from the db I want to convert it back to a string, so that it fits in the input field as a value.
I could not find the answer in the docs for autoform, collection2 or simple-schema (or at least did not understand it ;-)
Thanks for your help!
Use autoform hooks. The information that you are looking for is here:
https://github.com/aldeed/meteor-autoform#callbackshooks
More specifically what you are looking for is this:
AutoForm.hooks({
someFormId: {
formToDoc: function(doc) {
// Called every time an insert or typeless form
// is revalidated, which can be often if keyup
// validation is used.
},
docToForm: function(doc, ss) {
// Called whenever `doc` attribute reactively changes, before values
// are set in the form fields.
}
});
The doc can be modified in each hook specifically when it is going to the collection and when it is being reactively pulled from the collection.

Proper way to use .populate() mongodb Node.js

I am trying to attach the full user model to each comment in my comments section. Since mongodb doesn't have joins I have been trying to figure out how to use .populate() to add the user object from the controller. As I understand it, to use the populate function there are only two things you must do.
Define a ref in the model.
Call .populate() on the name of the field which has a ref defined.
I have defined the ref in the model to the 'User' model:
var commentSchema = new mongoose.Schema({
author: { type: String, ref: 'User' },
description: String,
commentID: String,
hasParent: String,
parentComment: String,
timestamp: { type: Number, default: 0 },
});
Then I am trying to add the user model to the author field in the comment object:
Comment
.find()
.populate('author')
.exec(function (err, comments) {
console.log(comments);
});
Is there a step that I am missing? I was hoping to see all the comments with the full user object in the author field of each comment.
The solution was deleting the comments collection from mongodb. Starting fresh it works as expected. If anyone runs into a similar problem try deleting the collection of objects that were created before you had added a ref to the model.
Please consider making following changes.
author: { type: String, ref: 'User' };
authot: { type: Schema.Types.ObjectId, ref:'User'};

Categories