Search for an array match in MongoDB - node.js

I have a schema like this in Mongo DB:
MAIN:
var mySchema= new mongoose.Schema({
username: { type: String, unique: true },
custom_schema: [mongoose.modelSchemas.Custom]
});
app.db.model('Main', mySchema);
My Custom schema looks like this:
var custom_schema = new mongoose.Schema({
my_string:{type:String, default: '' },
somefield: {
//
}
});
app.db.model('Custom', custom_Schema);
I need to retrive all records from the data base whose custom_schema contains a specific string in the string my_string.
I am doing this right now:
var filters = {};
filters.somefield=new RegExp('^.*?'+city+'.*$', 'i');
req.app.db.models.Main.pagedFind({
filters: filters,
keys: 'mykeys',
limit: 1000,
page: 1,
sort: '_by something'
}, function(err, results) {
if (err) {
return next(err);
}
});
How do I add a filter to search for a particular string in the array of custom_Schema?

To search inside object in mongodb, you need to use same directives as you do in regular Object object.var, so your filters should look like filters['custom_schema.somefield'] = ...

Related

node.js: Not able to auto increment the field in an array

I have been working on a project and creating an rest api with the use of nodejs and mongodb. I am trying to auto increment a field that increases by 1 when a new entry is there in the database. I am using mongoose auto increment library for that. I am able to increment a field when it is in the schema. However it fails when it increment a field that is present in the array and the array is included in the schema.
Here is my code:
const healthinfo = mongoose.Schema({
cur_madications:[{
sno:{type:String},
medicinename:{ type: String, require: true },
dosage:{ type: String, require: true },
}],
healthinfoid:{type:String},
medicalhistory: { type: String, require: true },
product: { type: String },
clientid:{type:String}
});
var HealthInfo = module.exports = mongoose.model('HealthInfo', healthinfo);
autoIncrement.initialize(mongoose.connection)
healthinfo.plugin(autoIncrement.plugin, {
model: 'healthinfo',
field: 'sno',
startAt: 1,
incrementBy: 1
});
module.exports.addhinfo = function (data, callback) {
data.save(callback);
}
module.exports.getAllhinfo = function (callback) {
HealthInfo.find(callback);
}
module.exports.update = function (updateobj, callback) {
HealthInfo.update({ _id: updateobj._id},{ $set: updateobj }, callback);
}
module.exports.deletehinfo = function (delobj, callback) {
HealthInfo.deleteOne({ _id: delobj }, callback);
}
// module.exports.getById = function (ids, callback){
// HealthInfo.find({'clientid': { $in: ids}}, callback);
// }
module.exports.getById = function (clientid, callback){
HealthInfo.find({'clientid' : clientid}, callback);
}
I am trying to increment the field sno that is present in cur_medication array. But due to some reason I am not getting the desired result. Any help would be appreciated.
Change the type of sno from string to number
sno:{type:Number}
Hope this solves your problem

Autoincrement with Mongoose

I'm trying to implement an autoicremental user_key field. Looking on this site I came across two questions relevant for my problem but I don't clearly understand what I should do. This is the main one
I have two Mongoose models, this is my ProductsCounterModel.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var Counter = new Schema({
_id: {type: String, required: true},
sequence_value: {type: Number, default: 0}
});
module.exports = mongoose.model('products_counter', Counter);
and this is the Mongoose model where I try to implement the auto-increment field:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var products_counter = require('./ProductsCounterModel.js');
var HistoricalProduct = new Schema({
product_key: { type: Number },
class: { type: String },
brand: { type: String },
model: { type: String },
description: { type: String }
});
HistoricalProduct.pre("save", function (next) {
console.log("first console log:",products_counter);
var doc = this;
products_counter.findOneAndUpdate(
{ "_id": "product_key" },
{ "$inc": { "sequence_value": 1 } },
function(error, products_counter) {
if(error) return next(error);
console.log("second console log",products_counter);
doc.product_key = products_counter.sequence_value;
next();
});
});
module.exports = mongoose.model('HistoricalProduct', HistoricalProduct);
Following the steps provided in the above SO answer I created the collection products_counter and inserted one document.
The thing is that I'm getting this error when I try to insert a new product:
"TypeError: Cannot read property 'sequence_value' of null"
This are the outputs of the above console logs.
first console log output:
function model (doc, fields, skipId) {
if (!(this instanceof model))
return new model(doc, fields, skipId);
Model.call(this, doc, fields, skipId);
}
second console log:
Null
can you see what I'm doing wrong?
You can run following line in your middleware:
console.log(products_counter.collection.collectionName);
that line will print products_counters while you expect that your code will hit products_counter. According to the docs:
Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName method. This method pluralizes the name. Set this option if you need a different name for your collection.
So you should either rename collection products_counter to products_counters or explicitly configure collection name in your schema definition:
var Counter = new Schema({
_id: {type: String, required: true},
sequence_value: {type: Number, default: 0}
}, { collection: "products_counter" });

Express: Embed document in the existing document

I am developing an application in Express, Node and Mongo being the database. I have a collection users, and user can have mutiple registered-IDs. It like a one-to-many relationship. I m trying to embed a document in the user collection like this:
post(function (req, res, next) {
var pid=req.body.pid;
var sid=req.body.sid;
var rfname=req.body.rfname;
var des=req.body.des;
var brand=req.body.brand;
var model=req.body.model;
var serial=req.body.serial;
var location=req.body.location;
var arr={pid: 'pid', sid: 'sid', rfname: 'rfname' ,des: 'des', brand: 'brand', model: 'model' ,serial: 'serial', location: 'location'};
mongoose.model('User').findOne({'pemail': req.session.email}, function (err, user){
if(err){
} else {
user.registeredId = arr;
user.save(function(err){
if(err){
} else {
res.render('user/register', {'success': 'dfhlaksdhfh'});
}
})
}
});
}
My user schema is like this:
var mongoose = require('mongoose');
var userSchema = new mongoose.Schema({
email: String,
password: String,
fname: String,
lname: String,
plang: String,
slang: String,
country: String,
state: String,
city: String,
postalcode: String,
address1: String,
address2: String,
pemail: String,
semail: String,
age: String,
gender: String,
pphone: String,
sphone: String,
q1: String,
a1: String,
q2: String,
a2: String,
cfname: String,
clname: String,
cemail: String
});
mongoose.model('User', userSchema);
Guide me, what am i doing wrong, because it does not embed document in the existing document. Do I need to define that in schema, if so, then how?
In your schema definition, the field registeredId is not defined and by default through the strict option, Mongoose ensures that values passed to your model constructor that were not specified in our schema do not get saved to the db, hence it is not creating the modified document.
You can either explicitly define the field in your schema or set the strict option to false in your schema definition:
// set to false..
var userSchema = new Schema({..}, { strict: false });
and then implement one of the findAndModify() methods like findOneAndUpdate() to update your user document by pushing the new object to the new array field registeredId. So you could re-write your post function as:
post(function (req, res, next) {
var User = mongoose.model('User'),
pid=req.body.pid,
sid=req.body.sid,
rfname=req.body.rfname,
des=req.body.des,
brand=req.body.brand,
model=req.body.model,
serial=req.body.serial,
location=req.body.location,
arr = {
'pid': pid,
'sid': sid,
'rfname': rfname,
'des': des,
'brand': brand,
'model': model,
'serial': serial,
'location': location
},
condition = { 'pemail': req.session.email },
update = {
"$push": { 'registeredId': arr }
};
User.findOneAndUpdate(
condition,
update,
function (err, doc){
if(err){}
else {
// doc contains the modified document
res.render('user/register', {'success': 'dfhlaksdhfh'});
}
}
);
});

Mongoose field match populate

I'm having some trouble with how best to implement this join/query in mongoose.
var mongoose = require('mongoose');
var addressSchema = new mongoose.Schema{
rank: Number,
address: String,
tag_name: String,
tag_url: String};
var tagSchema = new mongoose.Schema{
address: String,
name: String,
url: String};
I have a bunch of addresses saved and a bunch of tags saved. Some addresses have tags, most do not. I update the addresses and tags separately frequently. What I want to do is query some specific addresses and return them as an array with the tag fields filled in (the address tag fields are blank in the database).
So for example, I want to do this without making a db query for every address(101 db queries in the example). I'm not sure if $match or $in or populate is what I'm looking for. The below code is untested and may not work, but it should give you an idea of what I'm trying to do.
var db = require('../config/dbschema');
// find an array of addresses
db.addressModel.find({ rank: { $lte: 100 } }, function(err, addresses) {
// now fill in the tag fields and print
addTagField(0, addresses);
});
// recursive function to fill in tag fields
// address tag name and tag url fields are blank in the database
function addTagField(n, addresses) {
if(n < addresses.length) {
db.tagModel.find( { address: addresses[n].address }, function(err, tag) {
// if find a tag with this address, fill in the fields
// otherwise leave blank and check next address in array
if(tag) {
addresses[n].tag_name = tag.name;
addresses[n].tag_url = tag.url;
}
addTagField(n+1, addresses);
});
} else {
console.log(addresses);
}
}
http://mongoosejs.com/docs/api.html#aggregate_Aggregate-match
http://mongoosejs.com/docs/api.html#query_Query-in
http://mongoosejs.com/docs/api.html#document_Document-populate
I want to do what the above does with fewer db queries.
Your major problem is that you're not taking advantage of Mongoose's relationship mapping. Change your schemas just a bit and your problem will easily be solved. You can do it like this:
var tagSchema = new Schema({
name: String,
url: String,
})
var addressSchema = new Schema ({
rank: Number,
address: String,
tags: [tagSchema],
})
addressModel.find({rank: {$lte: 100}}, function(err, addresses) {
...
})
or this:
var tagSchema = new Schema({
name: String,
url: String,
})
var addressSchema = new Schema ({
rank: Number,
address: String,
tags: [{type: ObjectId, ref: 'Tag'}],
})
addressModel
.find({rank: {$lte: 100}})
.populate('tags', 'name url')
.exec(function(err, addresses) {
...
})
I didn't want to embed the docs. The is what I came up with.
db.addressModel.find({ rank: { $lte: 100 } }, function(err, addresses) {
if(err) return res.send(400);
if(!addresses) return res.send(404);
var addrOnlyAry = addresses.map(function(val, idx) { return val.address; });
db.tagModel.find( { address: { $in: addrOnlyAry } }, {}, function(err, tags) {
if(err) return res.send(400);
if(tags.length > 0) addresses = setTagFields(addresses, tags);
return res.json(addresses);
}
}
function setTagFields(addresses, tags) {
for(var i=0; i < tags.length; i++) {
for(var j=0; j < addresses.length; j++) {
if(addresses[j].address === tags[i].address) {
addresses[j].tag_name = tags[i].tag;
addresses[j].tag_url = tags[i].url;
break;
}
}
}
return addresses;
}

Setting a virtual field in a Model based on an async query from another model

I want to have a user setting (in a user model) that is derived from the sum of values in another model.
What I have tried to do is create a virtual value using a query like this:
var schemaOptions = {
toObject: {
virtuals: true
}
,toJSON: {
virtuals: true
}
};
/**
* User Schema
*/
var UserSchema = new Schema({
firstname: String,
lastname: String,
email: String,
username: String,
provider: String,
phonenumber: Number,
country: String,
emailverificationcode: {type:String, default:'verifyme'},
phoneverificationcode: {type:Number, default:4321 },
emailverified: {type:Boolean, default:false},
phoneverified: {type:Boolean,default:false},
}, schemaOptions)
UserSchema
.virtual('credits')
.get(function(){
//Load Credits model
var Credit = mongoose.model('Credit');
Credit.aggregate([
{ $group: {
_id: '5274d0e5a84be03f42000002',
currentCredits: { $sum: '$amount'}
}}
], function (err, results) {
if (err) {
return 'N/A'
} else {
return results[0].currentCredits.toString();
//return '40';
}
}
);
})
Now, this gets the value but it fails to work correctly (I cannot retrieve the virtual 'value' credits). I think this is because of the async nature of the call.
Can someone suggest the correct way to achieve this?
Once again many thanks for any input you can provide.
Edit:
So I am trying to follow the suggested way but no luck so far. I cannot get my 'getCredits' method to call.
Here is what I have so far:
UserSchema.method.getCredits = function(cb) {
//Load Credits model
var Credit = mongoose.model('Credit');
Credit.aggregate([
{ $group: {
_id: '5274d0e5a84be03f42000002',
currentCredits: { $sum: '$amount'}
}}
], function (err, results) {
cb(results);
}
);
};
var User = mongoose.model('User');
User.findOne({ _id : req.user._id })
.exec(function (err, tempuser) {
tempuser.getCredits(function(result){
});
})
Any ideas? Thanks again
There are a few issues with your implementation:
UserSchema.method.getCredits
^^^^^^ should be 'methods'
Also, you have to make sure that you add methods (and virtuals/statics) to your schema before you create the model, otherwise they won't be attached to the model.
So this isn't going to work:
var MySchema = new mongoose.Schema(...);
var MyModel = mongoose.model('MyModel', MySchema);
MySchema.methods.myMethod = ... // too late, model already exists
Instead, use this:
var MySchema = new mongoose.Schema(...);
MySchema.methods.myMethod = ...
var MyModel = mongoose.model('MyModel', MySchema);
I would also advise you to always check/propagate errors.

Resources