MongoDB parent / child relationinship and fast retrieval of parents - node.js

I have the following models in mongoose, Book which belonds to a User.
When I Book.findAll(), I also want to retrieve information from the associated User. What is the best way to achieve this?
const user_schema = new mongoose.Schema({
fullname: { type: String },
avatar: { type: String }
});
module.exports.User = mongoose.model("User", user_schema);
const book_schema = new mongoose.Schema({
_uid: { type: ObjectId, ref: "User", required: true },
title: { type: String, required: true }
text: { type: String, required: true }
});
module.exports.Book = mongoose.model("Book", book_schema);
I need to be able to do something like:
Book.findOne({...}).user.avatar

You could use $lookup for this kind of operations.
db.books.aggregate([
{
$lookup:
{
from: "users",
localField: "user_id", //User ID in users collection
foreignField: "_uid", //User ID in books collection
as: "books_data"
}
}
])
Also, kindly have primary key values in both schemas:
const user_schema = new mongoose.Schema({
user_id : {type : String },
fullname: { type: String },
avatar: { type: String }
});

Related

How to query documents with reference fields parameters in Mongoose?

I have the following schemas (Product, ProductCategory):
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
productCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ProductCategory'
}
})
const productCategorySchema = new mongoose.Schema({
name: {
type: String,
required: true
}
})
What I would like to do is to query all the Product documents who has a certain Product.productCategory.name = ?
I read about population in mongodb but can't really know how to apply it here.
You could use aggregation function '$lookup'
db.productCategory.aggregate([{
$match: {
name: "{category_name}"
}
}, {
$lookup: {
from: 'product',
localField: '_id', // ProductCategory._id
foreignField: 'type', // Product.product_category_id
as: 'products'
}
}]);

Query multiple collections Nodejs

Hello im trying to join these collections i want to get all users which has "active" attribute equal to false. I couldn't figure out how to acquire this query. There are my schemas:
User Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
type: {
type: String,
required: true
},
active:{
type:Boolean
}
});
module.exports = mongoose.model('users', UserSchema);
Company Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CompanySchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
companies: [{
name: {
type:String
},
country:{
type:String
}
}
]
});
module.exports = Company = mongoose.model('company', CompanySchema);
Note: Not all users have companies only the type "client" and i want to get both, "client" and "employe"
You may want to refactor your Schema to better accommodate the type of data you have available.
For example:
User Schema:
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
type: {
type: String,
required: true
},
active:{
type:Boolean
},
companies: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'company'
}]
});
And Company Schema:
const CompanySchema = new Schema({
name: {
type:String
},
country:{
type:String
}
});
Then to get a list of all users who are active, and automatically populate any company data for those users (Assuming your user model is called UserModel)
UserModel.find({ active: false }).populate('companies').exec();
If you are unable to edit your data structure for any reason, then you could perform a query similar to:
CompanyModel.aggregate([
{ $lookup: { from: 'users', localField: 'userId', foreignField: '_id', as: 'user' } },
{ $match: { '$user.active': false } }
]).exec()
This will perform an aggregate lookup on the UserId field and then only match on ones where the active property is set to false.

Include count of subdocuments in schema

In my NodeJS project, I'm creating Mongoose schemas as below:
//Likes Schema
var likesSchema = mongoose.Schema({
postId: { type: mongoose.Schema.Types.ObjectId, ref: "Post", required: 'Provide the news ID to which this comment belongs' },
});
module.exports = mongoose.model('Likes', likesSchema);
//Post schema
var postSchema = mongoose.Schema({
title: { type: String, required: 'Kindly enter the title' },
description: { type: String, required: 'Kindly enter the description of the news' }
});
module.exports = mongoose.model('Post', postSchema);
Post is a schema that has a title and a description. Like is a schema that tracks number of likes for a particular post. So it has just postID.
Now I want to include "count" of likes as a variable in "Post" schema. I don't want to count the likes during query execution.
Is there any simple way to achieve it?
I found a solution after doing some trial and errors:
db.Post.aggregate([
{
$lookup: {
from: "Likes",
localField:"_id",
foreignField: "postId",
as: "likes"
}
},
{
$project: {
title: 1,
description: 1,
count: { $size: "$likes" }
}
}
]).pretty()

Mongoose WHERE OR LIKE syntax on referenced schemas

I have the following 2 schemas (Clinic and User):
const ClinicSchema = new Schema({
name: {
type: String,
unique: true,
required: true
},
createdBy: {
type: Schema.Types.ObjectId,
ref: 'user'
},
createdAt: Date,
updatedBy: {
type: Schema.Types.ObjectId,
ref: 'user'
},
updatedAt: Date
});
And Here is the user Schema
const UserModelSchema = new Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
roles: {
type: [String],
required: true,
}
});
I want to write a query that will search a string that is contained in the clinic name OR createdBy(user) name OR createdBy(user) last name, and return all the clinics where either the clinic name matches part of the search string OR the created by name matches part of the search string OR the created by last name matches part of the search string here is the pseudo SQL alternative of what I am trying to explain:
SELECT * FROM clinics
JOIN users on clinics.createdBy = users.id
WHERE clinics.name LIKE '%STRING%'
OR users.firstname LIKE '%STRING%'
OR users.lastname LIKE '%STRING%'
I have been searching for this solution for the past 2 days and can't seem to be able to figure it out, more specifically I am trying to add the WHERE OR functionality to the following query:
const clinicsQuery = Clinic.find({
name: new RegExp(req.query.searchTerm, 'i')
});
....
const clinicsList = await clinicsQuery
.limit(limit)
.skip(skip)
.populate('createdBy', ['firstName', 'lastName']);
It can be performed with a $lookup
const clinicsQuery = Clinic.aggregate([{
$lookup:
{
from: 'user',
localField: 'createdBy',
foreignField: '_id',
as: 'user'
}},
{ $unwind: "$user"}
{
$match: {
$or: [
{"user.firstname": new RegExp(req.query.searchTerm, 'i')},
{"user.lastname": new RegExp(req.query.searchTerm, 'i')},
{ name: new RegExp(req.query.searchTerm, 'i')}
]
}
},
{$limit: limit}
]);
User will be in "user" field in the result. But you won't have a mongoose ready object :/

Mongoose find documents where field = req.body.user

I have a user schema and a post schema, wherein a user has many posts. I would like to return all posts that the user has on a route called '/post/dashboard'.
Here is my schemas:
let UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
default: null,
},
profile_pic: {
type: String,
default: '/img/profilepic.png',
},
posts: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
})
let PostSchema = new Schema({
title: {
type: String,
},
description: {
type: String,
}
original_poster: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
tags: {
type: [String]
}
})
So, for example something like:
app.get('/', (req,res) => {
Post.find({ original_poster: req.session.user }).then((posts) =>{
res.send(JSON.stringify(posts));
}) //where req.session.user is an id (the logged in user's object id or _id)
})
Essentially in sql syntax it might be something like:
SELECT * FROM POSTS WHERE ORIGINAL_POSTER = <req.session.user>
What is the proper way to return all posts by the req.session.user?
It seems that original_poster field represent a reference to User's model, If req.session.user is stored as a string you have to cast it to objectID:
const mongoose = require('mongoose');
...
let userId = mongoose.Types.ObjectId(req.session.user);
Post.find({ original_poster: userId }).then((posts) => {
res.send(JSON.stringify(posts));
});

Resources