Mongoose join two different collections with different foreign key - node.js

Users Schema:
{
username: "milkeypony",
_id: "_mongodbID",
id: "random_30_characters_string"
...
}
Blog Schema
{
title: "_title",
_id: "_mongodbID",
author: "random_30_characters_string"
...
}
The Blogs.author is the same ID as with in Users.id
And what I'm trying to do is when I use Blogs.findOne() to fetch some blog post, Mongoose will also help me fetch some user data.
And I already successfully done this with raw Mongo shell command
db.blogs.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "id",
as: "author"
}
}
])
And I try the mongoose populate method, but it didn't work out for me

make sure Blogs schema like have
author:{
type:Schema.Types.ObjectId,
ref: 'Users'
}
and populate like below
Blogs.findAll({})
.populate({
path:author
})
.exec((err, blogs)=>{
console.log(err,blogs);
}))
more info check offical doc

Related

Mongoose sorting by populated field

In my Task schema I have fields like this:
{
name: String
},
{
user: ObjectID
}
I need to sort tasks. If I want to do it by name field it's easy:
await Tasks.find().sort({name: 1})
That works. But the problem is when I want to sort by user.name. To get fields from User I can populate them, so:
await Tasks.find().populate('user', 'name').sort({'user.name': 1})
And it doesn't work. I cannot sort by fields added by populate function. I've been searching a lot in documentation and in other users' questions. I've found that I can pass sorting option to populate function but it doesn't work also. I guess it sort fields in populated field.
When I've tried to use aggregate with lookup like this:
Tasks.aggregate([{ $lookup: {
{
from: 'User',
localField: 'user',
foreignField: '_id',
as: 'someField'
}}}])
it returns someField: []
Can somebody help me? Thanks a lot!
In aggregate query, you should reference your collection with it's real name, NOT with the model name. So, instead of from: 'User', it should be from: 'users':
Tasks.aggregate([
{
$lookup: {
from: 'users',
localField: 'user',
foreignField: '_id',
as: 'user'
}
},
{
$set: {
user: { $first: '$user' }
}
},
{
$sort: {
'user.name': 1
}
}
])

How to perform FIND, then perform AND, then perform OR with POPULATE in Mongoose

I have a query that I wish to perform something similar as:
Archive does not exists AND
Owner Email in schema is in Query OR
Owner Email in Populated's schema is in Query
Below is what I have tried as of my understanding
let docs = await Document.find({ archive: { $exists: false }})
.and([{ owner_email: { $regex: localQuery } }])
.or()
.populate('owner_id', null, {
email: { $regex: localQuery },
});
So what I wish to do is, I have two schema, the user and the documents, User sometimes is shared along [as a librarian], then I wish to return, both, which matches the populated email or the actual owner's email.
As mongoose's populate() method does not really "join" collections and rather makes another query to the database to populate after the find() operation, you can switch to an aggregation pipeline and use $lookup in order to match the email in the referenced field. So assuming your models look like:
const Document = mongoose.model('Document', {
name: String,
archive: String,
owner_email: String,
owner: {type: Schema.Types.ObjectId, ref: 'Person'}
});
const Person = mongoose.model('Person', {
firstName: String,
lastName: String,
email: String
});
Then, you can do:
const result = await Document.aggregate([
{
$lookup: {
from: Person.collection.name,
localField: "owner",
foreignField: "_id",
as: "referencedOwner"
}
},
{
$match: {
archive: {$exists: false},
$or: [
{"referencedOwner.email": {$regex: localQuery}},
{"owner_email": {$regex: localQuery}}]
}
}
]);
Here's a working example on mongoplayground: https://mongoplayground.net/p/NqAvKIgujbm

Query a document reference in mongodb if condition is met

I have two mongo collections within my database named user and order. Within the user collection, there is an array of object references to orders. (the code snippet is a reduced down version of my schema for each collection).
User ({
user_id
username
email
firstname
surname
...
orders : [{type: Schema.Types.ObjectId, ref: "ordersmodel"}]
})
Order ({
order_id
current_status
date_ordered
...
})
What I am looking to do is to access the order information for a specific user when passed a user_id. This was my thinking so far:
User.aggregate([
{
$lookup: {
from: 'order',
localfield: 'orders',
foreignField: 'order_id',
as: 'order'
}},
{
$unwind: '$order'
},
{$project: {
_id: 0,
order_id: '$order.order_id',
status: '$order.status'
}}
]).toArray();
and am not sure what to do next in order to return the orders for a specific user.
when you use ref in schema you can using populate like this:
let result = await User.findById(user._id).populate("orders").lean()
console.log(result )

How to get user name from user id in React.js

I have created this schema with mongoose
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const QuestionSchema = new mongoose.Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
question:{
type:String
},
name:{
type:String,
},
answerd:[
{
user:{
type:mongoose.Schema.Types.ObjectId,
ref:'users'
}
}
]
})
module.exports = Question = mongoose.model('question',QuestionSchema);
In my global state (REDUX) I have the state
const initialState = {
questions:[],
question:null,
loading:true,
error:{}
};
The element questions store question object which contains the name of the user who made the question, the question itself, and people who have answered.
Some where in a .js file I can get the id of users who have answered by simply
question.buzzed.map(user=> <h1> {user._id} </h1>),
but how is possible to get this user name, I also have a schema for user which have attributes such as name, id, ... etc
You should use the populate method. It is like join in SQL for mongoose, because it connects your answer to the user collection. Based on your code, it could look like this :
Question.find({}).populate("answerd")
or something like this:
Question.find().populate({ path: 'answerd', select: 'username' });
For more information please read the populate documentation
U can use $lookup.
var aggregate = [
{
$unwind: "$answerd"
},
{
$lookup: {
from: "users",
localField: "user",
foreignField: "_id",
as: "user"
}
}
];
Questions.aggregate(aggregate, function(err, users) {
})

How to rename path in response for populate

I have a query like this:
galleryModel.find({_id: galleryId})
.populate({
model: 'User',
path: 'objectId',
select: 'firstName lastName'
})
End response for objectId will be like this:
objectId: {
...
}
How can I change it to user in response without changing real path?
You can do this by virtual populate, introduced in mongoose version 4.5 . For that you need to define a virtual field in mongoose schema.
var GallerySchema = new mongoose.Schema({
name: String,
objectId: {
type: mongoose.Schema.Types.ObjectId
},
});
GallerySchema.virtual('user', {
ref: 'User',
localField: 'objectId',
foreignField: '_id'
});
Ans when you run find query, just populate it with user.
Gallry.find({_id: galleryId}).populate('user','firstName lastName').exec(function(error, gallery) {
console.log(error);
console.log(gallery);;
});
Above code is not tested in program, there may be typos, You can get more details about mongoose virtual populate on below link
http://mongoosejs.com/docs/populate.html

Resources