Mongoose select not working? - node.js

Here my schema :
var countries = new Schema({
name: String,
capital: String,
body: String,
flag: {data: Buffer, contentType: String },
population: String,
currency: String
});
I want to select the fields name and capital.
Here my query:
countries.find({}, 'name capital', { skip: rand, limit: 1}, function(err, result){
if (err) return handleError(err);
console.log('Country :' + result);
callback(null, result);
});
But result print the entire document.
result.name is undefined.
I tried different syntaxes, like
countries.find({}, {'name':1, 'capital':1}, ....
But nothing is selected.
Any ideas?

find() returns an array of results, even though you limit it to 1.
That's probably why your result.name is undefined. So to access your name or capital, you have to use:
console.log("Country name:", result[0].name);
console.log("Country capital:", result[0].capital);
Maybe you want to use findOne(), which doesn't return an array but the object directly.
And both syntaxes should work, name capital and { name: 1, capital: 1 }.

Related

Mongoose 'find' is returning an empty array when queried with conditional params

I have a collection of some tweets and I need to fetch the tweets specific to a particular user. I'm using mongoose 'Find' query to find the tweets. I'm taking the user_id from req.params and using it as conditional parameter but mongodb is returning an empty array.
I have tried debugging with console.log and user_id seems to be correct. Type of 'user_id' is String which is expected and i don't seem to find the reason as why an empty array is returned even though there are matching entries in my collection.
Moreover, when i use other conditional parameters(apart from user), entries are fetched with no problem.
app.get('/search/:id',(req,res) => {
var _id = req.params.id;
if(!users.includes(_id)){
res.status(400)
res.send('Sorry... User not present in the list')
}
else{
Tweet.find({user: _id}).then((tweets)=>{
res.status(200).send(tweets)
}).catch((e) =>{
res.status(500).send();
})
}
})
My mongoose Schema:
const Tweet = mongoose.model('Tweet',{
user:{
type: String,
trim:true
},
text:{
type: String,
required: true,
trim:true
},
verified:{
type: Boolean
}
})
"I have tried debugging with console.log and user_id seems to be correct. Type of 'user_id' is String which is expected and i don't seem to find the reason as why an empty array is returned even though there are matching entries in my collection. "
accoutring to your this statement you search for "user_id" is document but you can not define user_id in your mongoose schema
const Tweet = mongoose.model('Tweet',{
user:{
type: String,
trim:true
},
text:{
type: String,
required: true,
trim:true
},
verified:{
type: Boolean
}
})
use user instead of id
app.get('/search/:user',(req,res) => {
var user = req.params.user;
if(!users.includes(user)){
res.status(400)
res.send('Sorry... User not present in the list')
}
else{
Tweet.find({user: user}).then((tweets)=>{
res.status(200).send(tweets)
}).catch((e) =>{
res.status(500).send();
})
}
})

Store value of a subquery - mongoose

What im doing:
When I call getData() the backend server .find() all my data.
My documents:
My test document has an _id a name and stuff fields. The stuff field contains the _id to the data document.
My data document has an _id and a age field
My goal:
When I send the data to the frontend I don´t want the stuff field to appear with the _id, I want it to appear with the age field from the correspondingdata.
What I have:
router.route('/data').get((req, res) => {
Test.find((err, aval) => {
if (err)
console.log(err);
else{
var result = [];
aval.forEach(e => {
var age;
// Get the age, only 1
Data.findById(e.stuff, 'age', function (err, a) {
age = a.age;
});
result.push({name: e.name, age: age});
});
res.json(result);
}
});
});
I find all the test documents then, for each one of them, I find the age and put the result in the array. Finaly I send the result array.
My problem:
The age field on my result array is always undefined, why? Any solutions?
UPDATE 1 - The schemas
The test schema
var TestSchema = new Schema(
{
stuff: {type: Schema.Types.ObjectId, ref: 'Data', required: true},
name: {type: String, required: true}
}
);
The data schema
var DataSchema = new Schema(
{
age: {type: Number, required: true}
}
);
router.route('/data').get((req, res) => {
Test.find({})
.populate('stuff')
.exec((err, aval) => {
if (err) console.log(err);
res.json(aval);
});
});
Mongoose model has a populate property that uses the value in the model attribute definition to get data matching the _id from another model.
It's a scop problem with your code try this out :
Data.findById(e.stuff, 'age', function (err, a) {
result.push({name: e.name, age: a.age});
});
But as a better solution think to use the Aggregation Framework

MongoDB: how to insert a sub-document?

I am using sub-documents in my MEAN project, to handle orders and items per order.
These are my (simplified) schemas:
var itemPerOrderSchema = new mongoose.Schema({
itemId: String,
count: Number
});
var OrderSchema = new mongoose.Schema({
customerId: String,
date: String,
items: [ itemPerOrderSchema ]
});
To insert items in itemPerOrderSchema array I currently do:
var orderId = '123';
var item = { itemId: 'xyz', itemsCount: 7 };
Order.findOne({ id: orderId }, function(err, order) {
order.items.push(item);
order.save();
});
The problem is that I obviously want one item per itemId, and this way I obtain many sub-documents per item...
One solution could be to loop through all order.items, but this is not optimal, of course (order.items could me many...).
The same problem could arise when querying order.items...
The question is: how do I insert items in itemPerOrderSchema array without having to loop through all items already inserted on the order?
If you can use an object instead of array for items, maybe you can change your schema a bit for a single-query update.
Something like this:
{
customerId: 123,
items: {
xyz: 14,
ds2: 7
}
}
So, each itemId is a key in an object, not an element of the array.
let OrderSchema = new mongoose.Schema({
customerId: String,
date: String,
items: mongoose.Schema.Types.Mixed
});
Then updating your order is super simple. Let's say you want to add 3 of items number 'xyz' to customer 123.
db.orders.update({
customerId: 123
},
{
$inc: {
'items.xyz': 3
}
},
{
upsert: true
});
Passing upsert here to create the order even if the customer doesn't have an entry.
The downsides of this:
it is that if you use aggregation framework, it is either impossible to iterate over your items, or if you have a limited, known set of itemIds, then very verbose. You could solve that one with mapReduce, which can be a little slower, depending on how many of them you have there, so YMMB.
you do not have a clean items array on the client. You could fix that with either client extracting this info (a simple let items = Object.keys(order.items).map(key => ({ key: order.items[key] })); or with a mongoose virtual field or schema.path(), but this is probably another question, already answered.
First of all, you probably need to add orderId to your itemPerOrderSchema because the combination of orderId and itemId will make the record unique.
Assuming that orderId is added to the itemPerOrderSchema, I would suggest the following implementation:
function addItemToOrder(orderId, newItem, callback) {
Order.findOne({ id: orderId }, function(err, order) {
if (err) {
return callback(err);
}
ItemPerOrder.findOne({ orderId: orderId, itemId: newItem.itemId }, function(err, existingItem) {
if (err) {
return callback(err);
}
if (!existingItem) {
// there is no such item for this order yet, adding a new one
order.items.push(newItem);
order.save(function(err) {
return callback(err);
});
}
// there is already item with itemId for this order, updating itemsCount
itemPerOrder.update(
{ id: existingItem.id },
{ $inc: { itemsCount: newItem.itemsCount }}, function(err) {
return callback(err);
}
);
});
});
}
addItemToOrder('123', { itemId: ‘1’, itemsCount: 7 }, function(err) {
if (err) {
console.log("Error", err);
}
console.log("Item successfully added to order");
});
Hope this may help.

Mongoose, find, return specific properties

I have this get call:
exports.getBIMFromProject = function(req, res){
mongoose.model('bim').find({projectId: req.params['prj_id']}, function(err, bim){
if(err){
console.error(err);
res.send(500)
}
res.send(200, bim);
});
};
Where do I specify which properties I want to return? Can't find it in the docs. The above returns the entire object. I only want a few properties returned.
This is my schema:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var bimSchema = new Schema({
projectId: Number,
user: String,
items:[
{
bimObjectId: Number,
typeId: String,
position:{
floor: String,
room:{
name: String,
number: String
}
}
}
]
});
mongoose.model('bim', bimSchema);
I don't want the items array included in my rest call.
You use projection. The first example in the mongoose query docs has a projection operation tucked in.
NB: not real code b/c I highlighted the important bits with triple stars
// find each person with a last name matching 'Ghost', ***selecting the `name` and `occupation` fields***
Person.findOne({ 'name.last': 'Ghost' }, ***'name occupation'***, function (err, person) {
if (err) return handleError(err);
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
})
The Person schema isn't specified but I think the example is clear enough.
Mongoose provides multiple ways to project documents with find, findOne, and findById.
1. Projection as String:
// INCLUDE SPECIFIC FIELDS
// find user and return only name and phone fields
User.findOne({ email: email }, 'name phone');
// EXCLUDE SPECIFIC FIELD
// find user and return all fields except password
User.findOne({ email: email }, '-password');
2. Projection by passing projection property:
// find user and return just _id field
User.findOne({ email: email }, {
projection: { _id: 1 }
});
3. Using .select method:
// find user and return just _id and name field
User.findOne({ email: email }).select('name');
// find user and return all fields except _id
User.findOne({ email: email }).select({ _id: 0 });
You can do the same with find and findById methods too.
MyModel.find({name: "john"}, 'name age address', function(err, docs) {
console.log(docs);
});
This will return fields - name, age and address only.
With the help of .select() this is possible.
If number of fields required are less from total fields then,
.select('projectId user') can be used
Else number of fields to be ignored are less from total fields then,
.select('-items') can be used.
So for getting a field, simply, space separated string of fields can be passed and for ignoring the field, space separated string with "-" before the field can be used.
For more documentation.
Find Specific properties also avoid some properties
await User.find({ email: email }, { name: 1, phone: 1, status: 1, password: 0 });
.Select() method is used to select which fields are to be returned in the query result
let result = await MyModel.find({ user : "user" }).select('name lastname status')

Mongoose error - elemMatch() must be used after where() when called with these arguments

I have the following schemas:
UserSchema = new Schema({
'username': { type: String, validate: [validatePresenceOf, 'a username is required'], index: { unique: true } },
'hashed_password': String,
'salt': String,
'locations': [LocationSchema]
});
LocationSchema = new Schema({
'lat': Number,
'lng': Number,
'address': { type: String, validate: [validatePresenceOf, 'The address is required in order to add a new location.'] },
'name': String
});
I'm trying to find a particular single location document by it's id. To do this I'm attempting to query the users collection items by location key, which is an array of location documents. My query looks like this:
var query = User.find({"locations.$": 1})
.where()
.elemMatch({locations: {_id : ObjectId('531283690315992f05bcdc98')}})
.exec(function(err, data){
console.log(err);
console.log(data);
});
When it runs I get the following error:
Error: elemMatch() must be used after where() when called with these arguments
What does this mean? I can't seem to find a good explanation.
Forgot to mention, I can get the data I want from the mongo shell by running the following: db.users.find({locations: {$elemMatch : {_id : ObjectId('531283690315992f05bcdc98')}}}, {"locations.$": 1});
You need to provide a path to your where call and reorder things a bit:
User.find()
.where('locations')
.elemMatch({_id : ObjectId('531283690315992f05bcdc98')})
.select({'locations.$': 1})
.exec(function(err, data){
console.log(err);
console.log(data);
});
But you could also simplify it somewhat as you don't need to use $elemMatch here and you can let Mongoose take care of the casting:
User.find()
.where('locations._id', '531283690315992f05bcdc98')
.select({'locations.$': 1})
.exec(function(err, data){
console.log(err);
console.log(data);
});

Resources