Mongoose Sync local schema with mongoose document - node.js

Im trying to get this going or wondering if this is even necessary. I have a local schema I define for my user profiles. What im trying to do is write a code block that checks my local schema with what the database has. If a field is missing, add a default value.
My goal for this is to be able to add fields locally in my schema and have the database update when I add a new field.
My schema is as follows:
import mongoose, { Schema } from "mongoose";
const reqString = {
type: String,
required: true,
};
const reqNumber = {
type: Number,
required: true,
};
const userProfileSchema = new Schema({
//Discord User ID - Primary Key
_id: reqString,
wallet: reqNumber,
bank: reqNumber,
net_worth: reqNumber,
classID: reqNumber,
});
const name = "core-userprofile";
export default mongoose.models[name] ||
mongoose.model(name, userProfileSchema, name);
I have a class that pulls the user profile for the rest of the code to access. Id like a function in there that looks at the local schema, realizes there isnt classID, figures out its a type of number, and just places 0 there.
I believe from what im understanding I can use update/upsert when accessing it. Im more wondering if there is a way to sync these two and add/delete anything that doesn't match the local version.
Thanks in advance!

Related

Mongodb Collection name

I've created a database in MongoDB using mongoose. Although everything works fine, but when I check mongodb the name of the collection has extra 's' in its name. The collection name created is employees. What could be wrong, or is it just the naming convention of mongoose?
const mongoose = require('mongoose');
let employeeSchema = mongoose.Schema({
name: String,
email: String,
department: String,
doj: Date,
address: String
});
const Employee = mongoose.model("employee", employeeSchema);
module.exports = Employee;
It doesn't just add an extra 's' but it makes the correct plural of the name.
For Example : Mouse will be converted to mice
You can disable it by:
mongoose.pluralize(null);
Reference Link: https://github.com/Automattic/mongoose/issues/5947
So you have two options for controlling document names in mongoose.
If you just want to disable pluralization, you can do it with mongoose.pluralize(null) as in Ankit's answer.
And if you want to change your collection name whatever you want, you can do:
mongoose.model("employee", employeeSchema, { collection: 'myEmployee' } )

Storing form data in MongoDB (Design Question)

Trying to design a Form Entry web app and i've rarely used MongoDB before.
Wondering if this is the best practice for storing form (document) data inside a collection.
const mongoose = require('mongoose');
// Create Schema and Model
const documentSchema = mongoose.Schema({
nps: [{ // New Promotion Submission
documentId: Number,
orgid: Number,
documentFields: [{ // Form Fields
id: Number,
dateTimeSubmitted: Date,
title: String,
productDescription: String,
productUnitSize: Number,
productCartonQty: Number
}]
}]
})
const documents = mongoose.model('documents', documentSchema);
module.exports = documents;
This is absolutely fine design, couple of things to look at:
Make sure you introduce validation on your schema fields, mirror the same validation pattern on the frontend form fields also.
Be consistent with your naming: if you use camelCase in documentId make sure to also origId
Convention says you name a model in singular form, i.e. "Document" not "documents".
If you're going to re-use the documentFields schema anywhere else in other models, make sure to store it as a separate schema and import as needed.

Mongoose: ref custom field name

I am configuring Mongoose to work on an existing MongoDB, that has these two collections:
Users - with fields:
_id: ObjectId
name: String
org_id: ObjectId
Organizations - with fields:
_id: ObjectId
name: String
I want to be able to populate a User document by Organization data.
So I've created these two Models:
const userSchema = new Schema({
name: String,
org_id: {
type: Schema.Types.ObjectId,
ref: 'Organization',
},
});
const User = mongoose.model('User', userSchema);
const organizationSchema = new Schema({
name: String,
code: String,
});
const Organization = mongoose.model('Organization', organizationSchema);
Since historically the ref field from User to Organization is called org_id (instead of just organization) the population of a user by the organization code is:
const user = await User.findById('5b213a69acef4ac0f886cdbc')
.populate('org_id')
.exec();
where user.org_id will be populated by Organization data. Of course I would be happier to have organization instead of org_id in both - populate method and the path (i.e. user.organizationd).
What is the proper way to achieve it without changing the existing documents?
I could create my Schema methods (instead of populate) and aliases, but I am looking for a more generic and elegant solution.
I understood that you don't want to change the existent documents, but for me, if this name of field doesn't make more sense you need to refactor.
Change the name of the field, organization instead of org_id.
For this you can use the $rename command: MongoDB $rename
db.getCollection('users').updateMany({},{$rename: { "org_id": "organization" }});
After this you will can call .populate('organization').
If it is impossible, I believe that you will not find a solution better than aliases.
Mongoose Documentation: Aliases
I will follow along your code.looks like you applied this: mongoose.Schema=Schema
you embedded Organization model into User. first lets extract organization details for each user.
//import User and Organization models
const main=async ()=>{
const user=await User.findById("placeUserId")//we get the user
const populated=await user.populate("org_id").execPopulate()//we populated organization with all properties
console.log(populated.org_id) }
in the above code, org_id was already referenced in the userSchema. we just reached org_id property and extracted. this was simple. next without changing any code in userSchema and organizationSchema i will find which user is in which organization with virtual property.
virtual property allows us to create virtual fields in the database. it is called virtual because we do not change anything. it is just a way that to see how two models are related.
for this we are gonna add a little code on the page where you defined you defined your organizationSchema file which i assume in models/organization.js. this code will describe the virtual field. it is kinda schema of the virtual field.
//models/organization.js
organizationSchema.virtual('anyNameForField',{
ref:"User", //Organization is in relation with User
localField:"_id"//field that Organization holds as proof of relation
foreignField:"org_id"//field that User holds as proof of relation
})
now time to write the function to find the user inside the organization.
const reverse=async ()=>{
const organization=await Organization.findById("")
const populated=await organization.populate("anyNameForField").execPopulate()
console.log(populated.anyNameForField) //i gave a stupid name to bring your attention.
}
very simple and elegant!

Query Parse.com migrated database pointer relationship with Mongoose

Context
So we have migrated from Parse.com to an hosted MongoDB database. Now I have to write a script that queries our database directly (not using Parse).
I'm using nodejs / mongoose and am able to retrieve these documents.
Problem
Here is my schema so far:
var StorySchema = new mongoose.Schema({
_id: String,
genre: String
});
var ActivitySchema = new mongoose.Schema({
_id: String,
action: String,
_p_story: String /* Also tried: { type: mongoose.Schema.Types.ObjectId, ref: 'Story' } and { type: String, ref: 'Story' }*/,
});
I would like to write a query that fetches theses documents with the related Story (stored as a pointer).
Activity
.find({
action: 'read',
})
.exec(function(error, activities) {
activities.forEach(function(activity) {
// I would like to use activity._p_story or whatever the mean to access the story here
});
});
Question
Is there a way to have the fetched activities populated with their story, given that the _p_story field contains Story$ before the object id?
Thanks!
One option I have been looking at is the ability to create a custom data type for each pointer. The unfortunate side is Parse treats these as 'belongsTo' relationships and but does not store the 'hasMany' relationship that Mongoose wants for populate(). But once this is in place you can easily do loops to get the relational data. Not ideal but works and is what populate is really doing under the hood anyways.
PointerTypeClass.js -> This would work for populating the opposite direction.
var Pointer = function(mongoose) {
function PointerId(key, options) {
mongoose.SchemaType.call(this, key, options, 'PointerId');
}
PointerId.prototype = Object.create(mongoose.SchemaType.prototype);
PointerId.prototype.cast = function(val) {
return 'Pointer$' + val;
}
return PointerId;
}
module.exports = Pointer;
Also be sure mongoose knows about the new type by doing mongoose.Schema.Types.PointerId = require('./types/PointerTypeClass')(mongoose);
Lastly. If you are willing to write some cloudcode you could create the array of ids for your populate to know about the objects. Basically in your Object.beforeSave you would update the array of the id for the relationship. Hope this helps.

Easy way to reference Documents in Mongoose

In my application I have a User Collection. Many of my other collections have an Author (an author contains ONLY the user._id and the user.name), for example my Post Collection. Since I normally only need the _id and the name to display e.g. my posts on the UI.
This works fine, and seems like a good approach, since now everytime I deal with posts I don`t have to load the whole user Object from the database - I can only load my post.author.userId/post.author.name.
Now my problem: A user changes his or her name. Obviously all my Author Objects scattered around in my database still have the old author.
Questions:
is my approuch solid, or should I only reference the userId everywhere I need it?
If I'd go for this solution I'd remove my Author Model and would need to make a User database call everytime I want to display the current Users`s name.
If I leave my Author as is, what would be a good way to implement a solution for situations like the user.name change?
I could write a service which checks every model which has Authors of the current user._id and updates them of course, but this sounds very tedious. Although I'm not sure there's a better solution.
Any pro tipps on how I should deal with problems like this in the future?
Yes, sometime database are good to recorded at modular style. But You shouldn't do separating collection for user/author such as
At that time if you use mongoose as driver you can use populate to get user schema data.
Example, I modeling user, author, post that.
var UserSchema = new mongoose.Schema({
type: { type: String, default: "user", enum: ["user", "author"], required: true },
name: { type: String },
// Author specific values
joinedAt: { type: Date }
});
var User = mongoose.model("User", UserSchema);
var PostSchema = new mongoose.Schema({
author: { type: mongoose.Scheam.Types.ObjectId, ref: "User" },
content: { type: String }
});
var Post = mongoose.model("Post", PostSchema);
In this style, Post are separated model and have to save like that. Something like if you want to query a post including author's name, you can use populate at mongoose.
Post.findOne().populate("author").exce(function(err, post) {
if(err)
// do error handling
if(post){
console.log(post.author.type) // author
}
});
One solution is save only id in Author collection, using Ref on the User collection, and populate each time to get user's name from the User collection.
var User = {
name: String,
//other fields
}
var Author = {
userId: {
type: String,
ref: "User"
}
}
Another solution is when updating name in User collection, update all names in Author collection.
I think first solution will be better.

Resources