I was trying to use bluebird + mongoose to query DevHiin instances for each Club respectively like the following code:
the data structure of DevHiin.clubs is:
, clubs : [{ type: Schema.Types.ObjectId, ref: 'Club' }]
and club_M is an instance of the Club schema:
User.findOne({"_id": req.user._id})
.populate('clubs', '-textIndex')
.exec(function (err, user) {
if (err) res.status(500).json(err);
if (user.clubs) {
var clubs_M = user.clubs;
for (c = 0; c < clubs_M.length; c++) {
var club_M = clubs_M[c];
if (club_M.lst > lstDate) {
serverData.clubs.push(club_M);
var ciinPromise = DevHiin.find({"clubs": {$elemMatch: club_M}, "ts": {"$gt": lastSyncTime}})
.populate('uId', '_id username', null, null) // get necessary user info for a hiin
.lean()
.sort("ts")
.limit(10)
.execAsync();
promises.push(ciinPromise);
}
}
Promise.all(promises).then(function (ciinArrays) {
serverData.ciinArrays = ciinArrays;
callback(ciinArrays);
}).catch(function (err) {
callback(err);
});
unfortunately, I got the following error info:
"Error: Can't use $__ with Array.
at SchemaArray.castForQuery (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/schema/array.js:188:13)
at SchemaArray.cast$elemMatch (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/schema/array.js:336:23)
at SchemaArray.castForQuery (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/schema/array.js:191:19)
at module.exports (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/cast.js:196:39)
at Query.cast (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/query.js:2350:10)
at Query.find (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/query.js:999:10)
at Query.exec (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/query.js:1984:17)
at Query.tryCatcher (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/bluebird/js/main/util.js:24:31)
at Query.ret [as execAsync] (eval at <anonymous> (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/bluebird/js/main/promisify.js:1:0), <anonymous>:12:23)
at EventEmitter.<anonymous> (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/controllers/sync.js:189:18)
at EventEmitter.<anonymous> (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/node_modules/mpromise/lib/promise.js:175:45)
at emitOne (events.js:77:13)
at EventEmitter.emit (events.js:169:7)
at Promise.safeEmit (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/node_modules/mpromise/lib/promise.js:81:21)
at Promise.fulfill (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/node_modules/mpromise/lib/promise.js:94:24)
at Promise.resolve (/Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/promise.js:113:23)
at /Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/lib/query.js:1174:16
at /Users/vince-fan/WorkSpaces/MEAN/V_Starter/node_modules/mongoose/node_modules/kareem/index.js:109:16
at doNTCallback0 (node.js:408:9)
at process._tickCallback (node.js:337:13)"
is this an bluebird promise error, or an mongoose error? how can I fix it?
From the code it looks like you are trying to query using $elemMatch on an array value specified as the query criteria expression. I believe you need to use the $in instead, this selects the documents where the value of the clubs field equals any value in the specified array, which is stored by the variable club_M in your case. Thus your query should look like this:
var ciinPromise = DevHiin.find({"clubs": {"$in": [club_M._id]}, "ts": {"$gt": lastSyncTime}})
for querying field holds an array, simple treat it as normal field query.
My fixed code:
var ciinPromise = DevHiin.find({"clubs": club_M._id, "ts": {"$gt": lastSyncTime}})
Related
I'd like to know if this kind of async/await approach with mongoose is correct. I still need to use .exec and then returning the promise with mongoose or I can leave things like this. Here my code snippet:
This is the user controller for example:
/* Func to update one user by id */
const updateUser = async (id, user) => {
const filter = {_id: id};
const update = {name: user.name, email: user.email};
const result = await User.findOneAndUpdate(filter, update, {new: true});
return result;
};
This is the route:
/* PATCH update user passing the id in params */
router.patch('/list/:id/update', async (req, res, next) => {
try {
const data = await usersController.updateUser(req.params.id, {
name: req.body.name,
email: req.body.email,
});
res.status(data ? 200 : 404).json({
result: data,
message: 'User updated',
});
} catch (e) {
res.status(500).json({
result: e.toString(),
});
}
});
Is this approach correct using mongoose or I need to use the async calling .exec().then().catch() after the query?
According to mongoose documentation, as far as functionality is concerned, these two are equivalent. However, they recommend using the exec because that gives you better stack traces:
const doc = await Band.findOne({ name: "Guns N' Roses" }); // works
const badId = 'this is not a valid id';
try {
await Band.findOne({ _id: badId });
} catch (err) {
// Without `exec()`, the stack trace does **not** include the
// calling code. Below is the stack trace:
//
// CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises"
// at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11)
// at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21)
// at model.Query.Query.then (/app/node_modules/mongoose/lib/query.js:4423:15)
// at process._tickCallback (internal/process/next_tick.js:68:7)
err.stack;
}
try {
await Band.findOne({ _id: badId }).exec();
} catch (err) {
// With `exec()`, the stack trace includes where in your code you
// called `exec()`. Below is the stack trace:
//
// CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises"
// at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11)
// at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21)
// at Context.<anonymous> (/app/test/index.test.js:138:42)
// at process._tickCallback (internal/process/next_tick.js:68:7)
err.stack;
}
I have a Mongoose model that has nested array and a subdocument.
I seem to be ok when posting to the object arrays/subdocument, but I'm having trouble with the .put
I hard coded the params for testing, just in case they were not coming in from PostMan for some reason.
The result I get from the above code is an empty array!
So I'm getting the right record and it creates the "phone" array, but does not populate.
.put(function(req, res){
Member.find({'_id':req.params.id}, function(err, member){
if(err)
res.send(err);
member.phone.push({
number: "78787878787",
phoneType: 2
})
member.save(function(err){
if(err)
res.send(err);
res.json(member);
});
});
});
I want to have an endpoint that simply adds another "phone" record.
Here is my model:
//DEPENDENCIES
var mongoose = require('mongoose');
var contactSchema = new mongoose.Schema({
name:{type:String},
age:{type:Number}
});
var phoneSchema = new mongoose.Schema({
number:{ type: String },
phoneType:{ type: Number }
})
var memberSchema = new mongoose.Schema({
firstname: {
type: String
},
lastname: {
type: String
},
phone:[phoneSchema],
contacts:[contactSchema]
});
//RETURN MODEL
module.exports = mongoose.model('member', memberSchema);
Now when I run my code I get the following undefined for "members":
{ id: '587bcbffe64e9f28a6894dd7' }
[ { _id: 587bcbffe64e9f28a6894dd7,
lastname: 'Stanley',
firstname: 'Dave',
__v: 0,
contacts: [ [Object] ],
phone: [] } ]
events.js:154
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'push' of undefined
find returns an array of documents, not just one document. Thats is why it is giving error when you are trying to do member.phone.
Use findOne instead of find as you are querying by _id, it will return only one matched document or null(if its not present), so its a better choice than find.
Also, its better to check if the result is null or not. member will be null if no such _id is present.
Member.findOne({'_id':req.params.id}, function(err, member){
if(err)
res.send(err);
else if(member!=null)
{
member.phone.push({
number: "78787878787",
phoneType: 2
});
member.save(function(err){...});
}
});
If you are keen on using find. Use member[0] (first element) instead of member.
Member.find({'_id':req.params.id}, function(err, member){
if(err)
res.send(err);
else if(member.length!=0)
{
member[0].phone.push({
number: "78787878787",
phoneType: 2
});
member[0].save(function(err){...});
}
});
Hope that helps you.
I have a collection which has one of it's fields as a unique field.
var ProductSchema = new mongoose.Schema({
code: {type:String, required:true},
name: {type:String, default:""}
});
ProductSchema.index({ code: 1}, { unique: true });
When I create a new document with a code that already exists, the server crashes instead of returning an error through the callback:
module.exports.create = function (params, callback){
var product = new ProductModel(params);
product.save(function(error){
console.log(error);
callback(error);
});
}
Is this (the crash) the expected behavior or mongoose should be returning an error through the callback and not crash?
here's the error I get when saving the second document with same code:
[..path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:245
throw message;
^
TypeError: Cannot read property '0' of undefined
at [...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection/core.js:114:55
at [...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1132:7
at [...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1846:9
at Server.Base._callHandler ([...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:445:41)
at [...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:478:18
at MongoReply.parseBody ([...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
at null.<anonymous> ([...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:436:20)
at EventEmitter.emit (events.js:95:17)
at null.<anonymous> ([...path]/server/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:201:13)
at EventEmitter.emit (events.js:98:17)
http://mongoosejs.com/docs/api.html#model_Model-save
Put an error handler on the save for the product directly:
ProductModel.on('error', function(){..});
I have an issue when attempting to use the find methods in mongoose when searching for an object.
When I do this:
var newObj = MyObject();
newObj.save(function(err) {
if(err) throw err;
});
MyObject.findOne({ _id: newObj._id }, function(err, obj) {
console.log(obj);
});
null is returned. However, if say for "_id" a value is used like so:
....({ _id: 'abc1233dff4f24f' }....
then the object is returned. Any ideas why this might be happening? It seems like anytime any variable is used, the returned value is null.
There's no reason to believe the save has completed before you try to find the object. You should do the find inside the callback:
newObj.save(function(err) {
if(err) throw err;
MyObject.findOne({ _id: newObj._id }, function(err, obj) {
console.log(obj);
});
});
I'm trying to update an existing document by increment a counter and pushing an object to an array.
Here's the schema for User:
var UserSchema = new Schema({
[...]
posts: {
totalWords: { type: Number, min: 0, default: 0 },
_entries: [
{
words: { type: Number, min: 0 },
body: { type: String },
date: Date
}
]
},
});
And here's the update code:
var newPost = {
words: req.body.words,
body: req.body.entry,
date: new Date()
};
User.findOne(req.user._id, function (err, user) {
var previous = user.posts.totalWords;
user.posts.totalWords = previous + newPost.words;
user.posts._entries.push(newPost);
user.save(function (err) {
if (err) return res.send(400, err);
return res.json(newPost);
});
});
I get the following error:
[TypeError: Object.keys called on non-object]
Any ideas on how to solve this?
Answering my own question
I was able to solve the problem by changing:
User.findOne(req.user._id, function (err, user) { [...] });
Into this:
User.findById(req.user._id, function (err, user) { [...] });
I think, if you would like to use findOne you need follow syntax:
User.findOne({'_id': req.user._id}, {}, function (err, user) {
...
});
Not sure about findById() vs. findOne(), but I've had problems with Mongoose objects returning Object.keys called on non-object while saving or updating with mal-formed data or data corresponding to an older schema. While initializing the document, Mongoose was expecting a subdocument of some kind, but the data in the database didn't match that.
For me it usually happens when I change schemas from a simple object (String, Number, etc.) to a more complex subdocument of some kind. The schema types are mixed up and the object won't load, not even to fix the problem. I had to go into my database using the native driver, search for mal-formed documents using the $type operator, and update them individually.