I am trying to use node & mongoose's populate method to kind of 'join' 2 collections on query. The following is my schema setup:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ShopSchema = new Schema({
ssss: { type: Schema.Types.ObjectId, required :true, ref: 'Stat' },
ratings: [RatingSchema]
});
var RatingSchema = new Schema({
stat: { type: Schema.Types.ObjectId, required :true, ref: 'Stat' }
}, {_id: false});
Also I have setup the Stat mongoose model so that the queries works without error (but the result is not what I expected).
I tried to perform the following queries:
ShopSchema.statics.load = function(id, cb) {
this.findOne({
_id: id
}).populate('ssss', '_id stat_id').exec(cb);
};
mongoose.model('Shop', ShopSchema);
This gives me the correct result and the ssss is correctly referenced.
The result is something like this .
"ssss":{"_id":"5406839ad5c5d9c5d47091f0","stat_id":1}
However, the following query gives me the wrong result.
ShopSchema.statics.load = function(id, cb) {
this.findOne({
_id: id
}).populate('ratings.stat', '_id stat_id').exec(cb);
};
mongoose.model('Shop', ShopSchema);
This gives me ratings.stat = null for all results. Could someone tell me what I did wrong? Thanks.
I just found the answer by trial and error.....
in the last example ShopSchema is declared before the RatingSchema. So I am guessing Mongoose doesn't know exactly what is happening inside RatingSchema and making the populate returns an error. So if you declare RatingSchema before the ShopSchema and the populate method is working like a charm..
Related
I actually searched a ton and I saw a ton of mentions of my problem here but none of the things I tried helped me fix the issue i'm having.
I have a Room Scheme that looks like this:
const ObjectId = mongoose.Schema.ObjectId;
const roomSchema = mongoose.Schema({
users: [{
type: ObjectId,
ref: 'User'
}],
messages: [{
type: ObjectId,
ref: 'Message',
}],
post: {
type: ObjectId,
ref: 'Post'
}
});
As you can see I have an array of users with ref to another schema Users
I'm trying to query all the Rooms that has a User ObjectId in it (search ObjectId in an array).
while I can easily get this with querying mongo from cmd using this:
db.users.find({users:ObjectId('THE_OBJECT_ID')});
when I try to get the same while using mongoose it fails with:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
Here is how my route and find looks like:
app.route('/rooms/list/:user_id')
.get((req, res) => {
var query = { users: "USER_ID" };
Room.find(query ).populate('messages').then((data) => {
res.status(200).json(data);
}).catch((err) => {
console.log(err);
});
})
I tried to create type of object ID and use it but it still doesn't work.
var mongoose = require('mongoose'),
userId = 'THE_USER_ID';
var id = mongoose.Types.ObjectId(userId);
and than
Rooms.find({'users': id });
but it still doesn't work.
I also tried altering my query search using $in, $elemmatch it worked on cmd but failed when querying using mongoose.
Any help would be appreciated.
Issue :
If you check this :
var query = { users: "USER_ID" };
(Or)
userId = 'THE_USER_ID';
var id = mongoose.Types.ObjectId(userId);
What are you trying to do here ? You are passing in string USER_ID or THE_USER_ID as input and trying to convert it to type of ObjectId(). But string inside ObjectId() has certain restrictions which is why mongoose is failing to convert passed in string value to ObjectId() and getting error'd out.
Try this code :
Code :
const mongoose = require('mongoose');
app.route('/rooms/list/:user_id')
.get((req, res) => {
var query = { users: mongoose.Types.ObjectId(req.params.user_id) };
Room.find(query).populate('messages').then((data) => {
res.status(200).json(data);
}).catch((err) => {
console.log(err);
});
})
Your input should be value of user_id (Which will be string) - Convert it to ObjectId() and then query DB. So value of user_id should be a string that obeys ObjectId()'s restrictions, You can take string from one of existing doc's ObjectId() & test your get api.
I have a schema through mongoose:
const mongoose = require('mongoose');
const recipeSchema = mongoose.Schema({
title: String,
chef: String,
updated: {type: Date, default: Date.now},
region: String,
ingredients: [String],
instructions: [String]
}, { collection: 'recipes' })
module.exports = mongoose.model('Recipes', recipeSchema);
I find the mongoose docs really difficult to understand. I am trying to search for a match of all substring within the 'ingredients' array. I read somewhere that it could be done like so:
.find({ingredients: 'ing1'}) // not working
.find({'ing1': {$in: ingredients}}) // not working
I find it pretty difficult to find in depth tutorials on mongoose as well. Im thinking about not using it at all anymore and just sticking to mongodb shell.
You can use a regex search to match substrings:
.find({ingredients: /ing1/})
The reason that you use mongoose is for testability.
Instead of having to work with a MongoDb instance, which, in Windows can be a pain with the .lock file and the service, mongoose creates the schema that you can test your code with.
The mongoose way is ideal for TDD/TFD.
Below is the model and the mocha test:
recipemodel.js
var mongoose = require('mongoose'),Schema=mongoose.Schema;
var RecipeSchema = new mongoose.Schema({});
RecipeSchema.statics.create = function (params, callback) {
'\\ params is any schema that you pass from the test below
var recipe = new RecipeSchema(params);
recipe.save(function(err, result) {
callback(err, result);
});
return recipe;
};
var recipemodel=mongoose.model('Model', RecipeSchema);
module.exports = recipemodel;
You don't need to describe the schema, mongoose will create it for you when you pass the values of the collection from a mocha test, for example!
The mocha test is below:
var mongooseMock = require('mongoose-mock'),
proxyquire = require('proxyquire'),
chai = require('chai'),
expect = chai.expect,
sinon = require('sinon'),
sinonChai = require("sinon-chai");
chai.use(sinonChai);
describe('Mocksaving a recipe ingredient', function () {
var Recipe;
beforeEach(function () {
Recipe = proxyquire('./recipemodel', {'mongoose': mongooseMock});
});
it('checks if ingredient '+'ing1' + ' saved to mongoose schema', function
(done) {
var callback = sinon.spy();
var recipe = Recipe.create({ title: "faasos", chef:
'faasos',region:'Chennai',ingredients:'ing1',instructions:'abc' },
callback);
expect(recipe.save).calledOnce;
expect(recipe.ingredients).equals('ing341');
done();
});
});
The call to a sinon spy is simply to ensure that the call to the data in the schema got saved (mock saved!) and that the 'save' method did get called at least once. This logic flow is in sync with your actual logic, as you would use in code, when the save on a mongodb collection would be made.
Simply change the value to 'ing1' to make the test pass when you run the test.
For an array type, pass the values as below:
var recipe = Recipe.create({ title: "faasos", chef:
'faasos',region:'Chennai',ingredients:'ing341,ing1',instructions:'abc' }, callback);
expect(recipe.save).calledOnce;
expect(recipe.ingredients).to.include('ing1');
Try this:
.ingredients.find((i) => i === "ing1")
for all elements in the ingredients array, it looks if the content, here a string element, is strictly equal to "ing1"
i have created model for the mongo collection like below. but it was giving me the collection output which saved in mongoDB.
var mongoose = require('mongoose'),
Schema = mongoose.Schema({
name: {
type: String
},
age: {
type: Number
},
})
module.exports = mongoose.model('container', Schema);}
But later when i changed the last line of the code which is
"module.exports = mongoose.model('container', Schema);"
to
"module.exports = mongoose.model('container', Schema, 'container');"
it worked properly. I check the mongoose document they say to use the previous line, then why didn't it worked.
your problem seems to be from using "Schema" as a variable name
var ContainerSchema = new mongoose.Schema({
...
});
and exporting
module.exports = mongoose.model("Container", ContainerSchema);
would work.
so I've been at it for like 4 hours, read the documentation several times, and still couldn't figure out my problem. I'm trying to do a simple populate() to my model.
I have a User model and Store model. The User has a favoriteStores array which contains the _id of stores. What I'm looking for is that this array will be populated with the Store details.
user.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UserSchema = new Schema({
username: String,
name: {first: String, last: String},
favoriteStores: [{type: Schema.Types.ObjectId, ref: 'Store'}],
modifiedOn: {type: Date, default: Date.now},
createdOn: Date,
lastLogin: Date
});
UserSchema.statics.getFavoriteStores = function (userId, callback) {
this
.findById(userId)
.populate('favoriteStores')
.exec(function (err, stores) {
callback(err, stores);
});
}
And another file:
store.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var StoreSchema = new Schema({
name: String,
route: String,
tagline: String,
logo: String
});
module.exports = mongoose.model('Store', StoreSchema);
After running this what I get is:
{
"_id": "556dc40b44f14c0c252c5604",
"username": "adiv.rulez",
"__v": 0,
"modifiedOn": "2015-06-02T14:56:11.074Z",
"favoriteStores": [],
"name": {
"first": "Adiv",
"last": "Ohayon"
}
}
The favoriteStores is empty, even though when I just do a get of the stores without the populate it does display the _id of the store.
Any help is greatly appreciated! Thanks ;)
UPDATE
After using the deepPopulate plugin it magically fixed it. I guess the problem was with the nesting of the userSchema. Still not sure what the problem was exactly, but at least it's fixed.
I think this issue happens when schemas are defined across multiple files. To solve this, try call populate this way:
.populate({path: 'favoriteStores', model: 'Store'})
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Group = new Schema({
name: { type: String, required: true },
members: [{ type: ObjectId, ref: 'User' }],
leader: { type: ObjectId, ref: 'User' },
// more fields to filter by
});
Group.method('role', function (user) {
if (this.leader === user) return "Leader";
else if (this.members.indexOf(user) >= 0) return "Member";
else return "Non-Member";
});
I'm a Mongoose newbie and my first real query was a tricky one. I need to select a set of groups an display group name and the role of the current user (user_id is stored in session variable).
Can I use the 'role' method in a Mongoose select query?
Perhaps I should use a custom Node stream and implement the role method there?
I'm note sure that my conditions (this.leader === user) or (this.members.indexOf(user) >= 0) are correct or efficient. I need to avoid loading the User object for every Group document.
Code example needed.
Thanks for your help!
what you want to use is the aggregation framework
http://docs.mongodb.org/manual/applications/aggregation/
You might need to dip down into the native driver for this query.