Suppose I have the following two schemas:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const schemaA = new Schema({
tag: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'schemaB',
},
],
});
module.exports = SchemaA = mongoose.model('schemaA', schemaA);
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const schemaB = new Schema({
name: {
type: String,
}
});
module.exports = SchemaB = mongoose.model('schemaB', schemaB);
How can I find all documents in SchemaA that have their tag field match the name of SchemaB?
I know that if I had the _id of SchemaB, I'd be able to do this as,
const schemaBId = 'some-_id-string'
const docs = await SchemaA.find({ tag: schemaBId })
But how can I do this, if I only have the name value?
first use lookup to get data of ModelB and then put these data as t after that make query with match to find where name of ModelB is test
ModelA.aggregate([
{
$lookup: {
from: 'collectionB',
localField: 'tag',
foreignField: '_id',
as: 't',
},
},
{
$match: { 't.name': 'test' },
},
]);
You can try using mongoose populate() function it might help you achieve exactly what you want. Here is the populate docs for quick reference.
I had spent hours trying to work out how to get records from a document's child array by a specific field, but I failed it.
I would like to pass a personId by a web service to find which meeting he/she has been invited to. As a result, I could track down whether the invitee has accept to join the meeting or not.
Basically, I have the following JSON output:
{
"status": "success",
"requestedAt": "2021-03-28T22:47:03+11:00",
"size": 1,
"meetings": [
{
"invitedMembers": [
{
"isJoined": false,
"_id": "605ffbc00a21ed718c992549",
"person": "a123",
"__v": 0
}
]
}
]
}
with a controller like this:
const memberId = "a123";
const meetings = await Meeting.find({
'invitedMembers.member': memberId
}).populate('invitedMembers');
a meeting model class like below:
const mongoose = require('mongoose');
const meetingSchema = new mongoose.Schema({
invitedMembers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'InvitedMembers'
}
]
});
const Meeting = mongoose.model(
'Meeting',
meetingSchema
);
module.exports = Meeting;
and a invitedMembers class like this:
const mongoose = require('mongoose');
const invitedMembersSchmea = new mongoose.Schema({
member: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Member',
required: true
},
isJoined: {
type: Boolean,
default: false
}
});
const InvitedMembers = mongoose.model(
'InvitedMembers',
invitedMembersSchmea
);
module.exports = InvitedMembers;
The Member schema only contains a basic personal information such as first name, last name and etc.
I ended up solving my own problem by using a different approach where I changed my data structure by adding invitedMembers as an embedding model in the meeting model and updated the person field in the invitedMembers schema to _id.
Updated Meeting model class:
const mongoose = require('mongoose');
const invitedMembersSchmea = new mongoose.Schema({
_id: {
type: String,
required: true
},
isJoined: {
type: Boolean,
default: false
}
});
const meetingSchema = new mongoose.Schema({
invitedMembers: [
{
type: invitedMembersSchmea
}
]
});
const Meeting = mongoose.model(
'Meeting',
meetingSchema
);
module.exports = Meeting;
As a result, I can find the invited member by ID using the following query:
const memberId = "a123";
const meetings = await Meeting.find({
'invitedMembers._id': memberId
});
I was googling for last two days but no success. I need to perform inner join in mongoose with two schema but not getting response my other collection.
My question is what am I missing in my code? Please help me with this.
I want to get result of class and subjects also.
exports.classSubjectList = async (req, res, next) => {
const obj = await ClassSubject.find().populate('classmodel').exec();
res.status(200).json({
success: true,
response: obj
});
};
//ClassSubjectModel
const mongoose = require('mongoose');
mongoose.Promise = global.Promise
const Schema = mongoose.Schema
const classModel = require('../class/classModel');
const subjectModel = require('../subject/subjectModel');
var classsubject = new Schema({
ClassId: String,
SubjectId : String,
IsShow: { type: Boolean, default : true},
classmodel: { type: mongoose.Schema.Types.ObjectId, ref: classModel },
subjectmodel: { type: mongoose.Schema.Types.ObjectId, ref: subjectModel },
});
//Class Model
const mongoose = require('mongoose');
mongoose.Promise = global.Promise
const Schema = mongoose.Schema
var classinfo = new Schema({
ClassName: String,
IsShow: { type: Boolean, default : true},
});
module.exports = mongoose.model('classinfo', classinfo);
//SUBJECT Model
const mongoose = require('mongoose');
mongoose.Promise = global.Promise
const Schema = mongoose.Schema
var subject = new Schema({
SubjectName: String,
IsShow: Boolean,
});
module.exports = mongoose.model('subject', subject);
result
[
{
"IsShow": true,
"_id": "5e1efc0f354849246c472cfe",
"SubjectId": "5e1da60bf52acb30b87e92c4",
"ClassId": "5e1ec13ed777bf28d01e2481",
"__v": 0
}]
You should define ref as the name of your schema & not the object reference
Do it like this
classmodel: { type: mongoose.Schema.Types.ObjectId, ref: 'classinfo' }
subjectmodel: { type: mongoose.Schema.Types.ObjectId, ref: 'subject' },
// here 'classinfo' & 'subject' are the names you defined your schema with
You should populate both if you want a proper inner join
const obj = await ClassSubject.find().populate('classmodel').populate('subject').exec();
You must store ids of class & reference in classmodel & subjectmodel
key of your document for this to work
Hope this helps
So I tried to migrate a new field to the mongoDB collections.
New field is a array that is filled with objects.
The migration runs and is successful, it even shows the new field when
looking the collections.
Problem comes when I try to add data to this field - it shows that the
field is undefined.
What should be done to overcome this problem?
Migration code:
exports.up = async function(db) {
await db
.collection('useractions')
.update({}, {
$set: {
history: []
}
}, {multi: true, upsert: false});
};
Code to populate the new field:
const bookId = req.body.bookId;
const timestamp = req.body.timestamp;
const userId = req.body.userId;
const container = {bookId, timestamp};
UserAction.update(
{ userId },
{$set: { history: container}},
(err, cb) => {
if(err)next({error: err});
res.status(200).json({
cb
})
})
EDIT:
Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userActionModel = new Schema({
userId: {
type: String
},
likes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Podcast',
default: []
}],
tags: {
type: [String],
default: []
},
orderedBook: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Show',
default: []
}]
})
module.exports = mongoose.model('userAction', userActionModel);
I want to get query of the mongoose in nodejs application as describe below out put.
user.js, comment.js and post.js are the model files I used.
user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var userSchema = new Schema({
nick_name:{type:String},
email: {
type: String,
trim: true,
required: '{PATH} is required!',
index: true,
},
},{ collection: 'user'});
var User = mongoose.model('User', userSchema);
module.exports = User;
comment.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var commentSchema = new Schema({
comment: type:String,
user_id:{
type:Schema.Types.ObjectId, ref:'User'
},
is_active :1
},{ collection: 'comment'});
post.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var postSchema = new Schema({
post: type:String,
user_id:{
type:Schema.Types.ObjectId, ref:'User'
},
is_active :1
},{ collection: 'post'});
wants to get out put as follows:
{
"nick_name":"prakash",
"email":"prakash#mailinator.com",
"comments":[
{
"comment":"this is a comment text1",
"is_active":1,
},
{
"comment":"this is a comment text2",
"is_active":1,
}
],
"posts":[
{
"post":"this is a post text1",
"is_active":1,
},
{
"post":"this is a post text2",
"is_active":1,
},
{
"post":"this is a post text3",
"is_active":1,
},
]
}
dependencies
"express" => "version": "4.7.4",
"mongoose" => "version": "4.4.5",
"mongodb" => "version": "2.4.9",
"OS" => "ubuntu 14.04 lts 32bit",
if query is not possible ,please suggests me a proper mongoose plugn.
but I don't want to any changes in user.js file and its userSchema object.
There are no 'joins' in Mongo. But what you would do is change your User Schema to store the ObjectId's of the Comment and Post documents in an array of your User. Then use 'populate' when you need the data with the user.
const userSchema = new Schema({
nick_name:{type:String},
email: {
type: String,
trim: true,
required: '{PATH} is required!',
index: true,
},
comments: [{ type: Schema.Types.ObjectId, ref:'Comment' }],
posts: [{ type: Schema.Types.ObjectId, ref:'Post' }]
}, {timestamps: true});
mongoose.model('User', userSchema);
Your query would then look something like this:
User.find()
.populate('comments posts') // multiple path names in one requires mongoose >= 3.6
.exec(function(err, usersDocuments) {
// handle err
// usersDocuments formatted as desired
});
Mongoose populate docs
It is possible .you should use aggregation.
it should work.
Initiate the variable
var mongoose = require('mongoose');
var userCollection = require('./user');//import user model file
var resources = {
nick_name: "$nick_name",
email: "$email"};
userCollection.aggregate([{
$group: resources
}, {
$lookup: {
from: "Comments", // collection to join
localField: "_id",//field from the input documents
foreignField: "user_id",//field from the documents of the "from" collection
as: "comments"// output array field
}
}, {
$lookup: {
from: "Post", // from collection name
localField: "_id",
foreignField: "user_id",
as: "posts"
}
}],function (error, data) {
return res.json(data);
//handle error case also
});
Of course it is possible, you just have to use populate, let me tell you how:
Import your schemas
var mongoose = require('mongoose');
var userSch = require('userSchema');
var postSch = require('postSchema');
var commSch = require('commentSchema');
Init all the necessary vars
var userModel = mongoose.model('User', userSch);
var postModel = mongoose.model('Post', postSch);
var commModel = mongoose.model('Comment', commSch);
And now, do the query
postModel.find({}).populate('User')
.exec(function (error, result) {
return callback(null, null);
});
commModel.find({}).populate('User')
.exec(function (error, result) {
return callback(null, null);
});
This way you get the user inside of your comment and your post, to get the post and comments inside of your user, you have to do 3 queries, one for the user, one for the comments and one for the post, and mix all together
you can consider using populate virtual with both comments and posts like docs (https://mongoosejs.com/docs/populate.html#populate-virtuals) in model User. And using like this:
User.find({filter}).populates('virtualComments').populate('virtualPosts')
Adding on to the answers above: What if we only want a few specific fields returned for the populated documents? This can be accomplished by passing the usual field name syntax as the second argument to the populate method:
Story.
findOne({ title: /casino royale/i }).
populate('author', 'name'). // only return the Persons name
exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Ian Fleming"
console.log('The authors age is %s', story.author.age);
// prints "The authors age is null"
});
Reference: https://mongoosejs.com/docs/populate.html#field-selection
Use aggregate to do your work in a single query which is almost like a join.
var mongoose = require('mongoose');
var userModel = require('./user');//import user model file
let result = await userModel.aggregate([
{
$match: {
user: mongoose.Types.ObjectId(req.body.user_id),//pass the user id
}
},
{
$lookup: {
from: "comments",//your schema name from mongoDB
localField: "_id", //user_id from user(main) model
foreignField: "user_id",//user_id from user(sub) model
pipeline: [
{
$project:{ //use to select the fileds you want to select
comment:1, //:1 will select the field
is_active :1,
_id:0,//:0 will not select the field
}
}
],
as: "comments",//result var name
}
},
{
$lookup: {
from: "post",//your schema name from mongoDB
localField: "_id", //user_id from user(main) model
foreignField: "user_id",//user_id from user(sub) model
pipeline: [
{
$project:{//use to select the fileds you want to select
post:1,//:1 will select the field
is_active :1,
_id:0,//:0 will not select the field
}
}
],
as: "posts",//result var name
}
},
{
$project:{//use to select the fileds you want to select
nick_name:1,//:1 will select the field
email:1,
_id:0,//:0 will not select the field
comments:1,
posts:1
}
}
])