find documents on the basis of array of object id in mongoose - node.js

I have a user model with two roles
Manager
Employee
Each manager has an manager idea that references the User model
I have a new model called Customers
The customers will either have user id of manager or employee
I want to create a controller to view the customers that are under the manager and the employees of that manager
let employees= await Employee.find({ managerId: req.user._id });
const customers= await Customer.find({
registeredBy: { $in: [employees._id] },
});
this is only returning the customers that are NOT registered by any employee or manager. What is the correct way to achieve this?
edit
This is how my customer collection looks like
_id:ObjectId('61e2a99752f9023ef0f9a1a6')
serialNo:"421A"
registeredBy:ObjectId('61d02ae060ccdd5a7a95813a')
If the manager is searching, the record of all the Customers having that manager id as registeredBy as well as the Customers having registerBy of the employees that have that *managerId in Employee schema should be shown

Related

insert and update multiple documents mongodb

I know there's a lot of threads about this subject - but i still didn't find anything that matches my situation.
There is a big database with companies and products schema's.
Each company uploads their products, (product has "company" field with the mongo id of the company)
My operation goes like this -
Company uploads array of products.
Each product has his own productId.
I need to insert all the products in to the DB but -
If i find product with the same productId - update it - if not - insert a new product.
Change some fields (change product "status" to "deleted") to all of the products that belongs to that company in the DB, that didn't match any of the product id's (from the array of products the user uploaded).
I'm searching for a better solution than the one i'm using, my process goes like this -
fetching all the company's products.
looping through all of the products - and updating a field (status = "deleted") building a mapping object for each product, so i can identify it and check if it exists in the uploaded array.
looping through the company's array (with async library), checking if product exists (in the mapping array), if exists - updating it, if not - create a new product.
It feels very sloppy and i'm sure there is a better way to do it.
the thing i tried to use is -
await Product.updateMany({ productStatus: { $ne: 'manual'} , company: {$eq: company._id}}, { $set: {status: 'deleted' }});
and then
Product.bulkWrite(
productsArray.map((row) =>
({
updateOne: {
filter: { productId: row.productId, company: row.company },
update: row,
upsert: true
}
})
)
)
It was super slow (my method was faster even thought it's more complicated)
If i find product with the same productId - update it - if not - insert a new product.
You can use upserts, which is a find-and-modify operation with upsert: true set. Pass the whole product document.
Change some fields (change product "status" to "deleted") to all of the products that belongs to that company in the DB, that didn't match any of the product id's (from the array of products the user uploaded).
You can use $nin to update the status on all products whose ids are not in the list of currently available products.

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!

Force database tablename for collection relationship in Sails

In a sails project, considering a model User and a model Role, with a relationship between User and Role :
// `User.js
module.exports = {
attributes: {
...
roles: {
collection: 'role',
dominant: true
},
...
}
}
For the the database representation, sails/waterline will create following tables :
table user,
table role,
table like user_roles__role_roles_role to represent the collection
I know we can force the name for the models USER and ROLE
(with the property 'tablename' : http://sailsjs.com/documentation/concepts/models-and-orm/attributes).
But how can we force the name the relationship table ? (Especially this name is quite long and tends to exceed limit).
Assuming this is a two-way relationship, and the Role model has a users collection, Sails will expect a table named role_users__user_roles, which has the role id first, user id second.
Your example table name would require User to be dominant and would require the Role model to have an attribute named roles_role that is a User collection.
To create your own join table, you can use the through association method and add a new model that represents the relationship, perhaps UsersRoles, and specify the tableName in that model definition.
Examples of the through association:
sails
docs
similar question
gist from comments in that question

Mongoose: How to find documents by sub-collection's document property value

I’m using Mongoose version 4.6.8 and MongoLab (MLab). I have a Mongoose schema called “Group” that has a collection of User subdocuments called “teachers”:
var GroupSchema = new Schema({
//…more properties here…//
teachers: [{
type: Schema.ObjectId,
ref: 'User'
}]
});
This is a document from the “groups” collection on MongoLab:
{
//…more properties here…//
"teachers": [
{
"$oid": "5799a9c759feea9c208c004c"
}
]
}
And this is a document from the “users” collection on MongoLab:
{
//…more properties here…//
"username": "bob"
}
But if I want to get a list of Groups that have a particular teacher (User) with the username of “bob”, this doesn’t work (the list of groups is empty):
Group.find({"teachers.username": "bob"}).exec(callback);
This also returns no items:
Group.find().where('teachers.username').equals('bob').exec(callback);
How can I achieve this?
Without some more knowledge of your set up (specifically whether you want anybody named Bob or a specific Bob whose id you could pick up first) - this might be some help although I think it would require you to flatten your teachers array to just their ID's, not single-key objects.
User.findById(<Id of Bob>, function(err, user){
Group.find({}, function(err, groups){
var t = groups.map(function(g){
if(g['teachers'].indexOf(user.id))
return g
})
// Do something with t
})
})
You can use populate to do that.
Try this:
Group.find({})
.populate({
path : 'teachers' ,
match : { username : "bob" }
})
.exec(callback);
populate will populate based on the teachers field (given path) and match will return only those who have username bob.
For more information on mongoose populate options, Please read Mongoose populate documentation.
I think the solution in this case is to get a teacher’s groups through the User module instead of my first inclination which was to go through the Groups module. This makes sense because it is in line with how modern APIs represent a one-to-many relationship.
As an example, in Behance’s API, an endpoint for a user’s projects is:
GET /v2/users/user/projects
And a request to this endpoint (where the User’s username is “matiascorea”) would look like this:
https://api.behance.net/v2/users/matiascorea/projects?client_id=1234567890
So in my case, instead of finding the groups by teacher, I would need to simply find the User (teacher) by username, populate the teacher’s groups, and use them:
User.findOne({username: 'bob'})
.populate('groups')
.exec(callback);
And the API call for this would be:
GET /api/users/user/groups
And a request to this endpoint would look like this:
https://example.com/api/users/bob/groups

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