Mongoose: ref custom field name - node.js

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!

Related

Mongoose Sync local schema with mongoose document

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!

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.

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.

Mongoose Populate Returning null due to Schema design?

I'm stuck with mongoose populate returning null. I have a very similar situation to another question where it seems to we working just fine, perhaps with one important difference:
The model I'm referencing only exist as a subdocument to another model.
Example:
// The model i want to populate
Currency = new Schema({
code: String,
rate: Number
});
// The set of currencies are defined for each Tenant
// A currency belongs to one tenant, one tenant can have multiple currencies
Tenant = new Schema({
name: String,
currencies: [Currency]
});
Product = new Schema({
Name: String,
_currency: {type: ObjectId, ref: 'Currency'},
});
Customer = new Schema({
tenant: {type: ObjectId, ref: 'Tenant'},
products: [ Product ]
});
Then I export the models and use them in one of my routes where what I would like to do is something like
CustomerModel.find({}).populate('products._currency').exec(function(err, docs){
// docs[0].products[0]._currency is null (but has ObjectId if not usinn populate)
})
Which is returning null for any given product._currency but if I don't populate i get the correct ObjectId ref, which corresponds to an objectId of a currency embedded in a tenant.
I'm suspecting I need currencies to be stand-alone schema for this to work., Ie not just embedded in tenant, but that would mean I get a lot of schemas referencing each other.
Do you know if this is the case, or should my set-up work?
If this is the case, I guess I just have to bite the bullet and have multitude of collections referencing each other?
Any help or guidance appreciated!

Twitter OAuth + Node + A MongoDB Collection: How to store users?

I've successfully retrieved a user's id and screen name from Twitter's oauth service, like so:
{ user_id: '12345678', screen_name: 'spencergardner' }
I am hoping to create a simple way for users to authenticate using Twitter (and soon Facebook, for example), so that they can add words they are interested in learning to their account. How do I now go about setting up "users" in a mongodb collection that will allow each user to have their own bank of words (and other data)?
If I understand you correctly, you are asking how you can store data with different structures in a mongo collection.
Well, you're in luck! Mongo does just that. You can store any different data structures in a mongo collection without having to "declare" the structure a priori. Just create a DBObject (if using the Java driver for example), add fields to it, and just save it. You can then retrieve it, and query the data to see what this specific users has, and anything you want in your application.
I use mongoose with nodejs to create a user model which you would then input the oauth data into and then you would be free to associate whatever data you wanted.
Once you've obtained the Oauth information you could create a new User associating the twitter data with that specific user model. The _id is automatically provided however in this case, you would use the user_id returned from twitter (assuming that is unique).
Here's an example schema:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var userSchema = new Schema({
_id: String,
screen_name: String,
words: Array
});
module.exports = mongoose.model('User', userSchema);
In future you would be able to query the database for a particular user, and authenticate a user when they return. You would also look to create a new User with something similar to the following:
new User({ _id: req.body.user_id,
password: req.body.screen_name,
words: []
}).save(function(err) {
if (!err) {
res.send("User added");
}
})

Resources