Query For All Array Matches in Mongoose - node.js

I have a feeling this may have been asked elsewhere, but am having a tough time finding an answer.
Basically, I have a MongoDB/Mongoose Schema that stores a list of registered users. I am building a GUI that can query for all records that meet certain conditions. In this case, I am wanting to let the GUI users select via checkboxes all the genders they'd like to include in the query.
If I were passing in just a string, I know I'd write it as follows:
User.find({"gender": gender}).exec(function(err, users){
if(err)
res.send(err);
res.json(users);
});
But since I'll possibly be passing in an array of options, is there a native Mongoose function I can use to query the following cases?
[Male]
[Male, Female, Other]
[Male, Other]
[Female]
etc.
What would be the best way to write this query in Mongoose?
Thanks!

You should use $in operator
var gender = [Male, Female, Other]
User.find({"gender": {$in: gender}})

Related

Mongoose display comments and stars(likes) for each post [duplicate]

In Mongoose, I can use a query populate to populate additional fields after a query. I can also populate multiple paths, such as
Person.find({})
.populate('books movie', 'title pages director')
.exec()
However, this would generate a lookup on book gathering the fields for title, pages and director - and also a lookup on movie gathering the fields for title, pages and director as well. What I want is to get title and pages from books only, and director from movie. I could do something like this:
Person.find({})
.populate('books', 'title pages')
.populate('movie', 'director')
.exec()
which gives me the expected result and queries.
But is there any way to have the behavior of the second snippet using a similar "single line" syntax like the first snippet? The reason for that, is that I want to programmatically determine the arguments for the populate function and feed it in. I cannot do that for multiple populate calls.
After looking into the sourcecode of mongoose, I solved this with:
var populateQuery = [{path:'books', select:'title pages'}, {path:'movie', select:'director'}];
Person.find({})
.populate(populateQuery)
.execPopulate()
you can also do something like below:
{path:'user',select:['key1','key2']}
You achieve that by simply passing object or array of objects to populate() method.
const query = [
{
path:'books',
select:'title pages'
},
{
path:'movie',
select:'director'
}
];
const result = await Person.find().populate(query).lean();
Consider that lean() method is optional, it just returns raw json rather than mongoose object and makes code execution a little bit faster! Don't forget to make your function (callback) async!
This is how it's done based on the Mongoose JS documentation http://mongoosejs.com/docs/populate.html
Let's say you have a BookCollection schema which contains users and books
In order to perform a query and get all the BookCollections with its related users and books you would do this
models.BookCollection
.find({})
.populate('user')
.populate('books')
.lean()
.exec(function (err, bookcollection) {
if (err) return console.error(err);
try {
mongoose.connection.close();
res.render('viewbookcollection', { content: bookcollection});
} catch (e) {
console.log("errror getting bookcollection"+e);
}
//Your Schema must include path
let createdData =Person.create(dataYouWant)
await createdData.populate([{path:'books', select:'title pages'},{path:'movie', select:'director'}])

Mongoose & MongoDB: Retrieve results narrowed by multiple parameters

I need to get data from MongoDB that is first narrowed by one initial category, say '{clothing : pants}' and then a subsequent search for pants of a specific size, using an array like sizes = ['s','lg','6', '12'].
I need to return all of the results where 'pants' matches those 'sizes'.
I've started a search with:
Product.find({$and:[{categories:req.body.category, size:{$in:req.body.sizes}}]},
function(err, products) {
if (err) { console.log(err); }
return res.send(products)
});
I really don't know where to go from there. I've been all over the Mongoose docs.
Some direction would be very helpful.
The mongoose queries can receive object like Mongodb would. So you can pass the search parameters separated by ,
Product.find({categories:req.body.category, size:{$in:['s','lg','6', '12']}})
For more information on $in, check here
For more information on $and operator, check here (note we can ommit the $and operator in some cases and that is what I did)

Loopback query which compares field values

Say i have the following Scheme
Product: {
Quantity: Number,
SelledQuantity: Number
}
Would it be possible to write a query where all the results returned are where Quantity=SelledQuantity?
If so, is there a way to use it when doing a populate? (Perhaps inside the match field in the opts object ?)
I use mysql connector.
yes as I understood your problem you can do this by following rest call.
http://localhost:3000/api/products?filter[where][SelledQuantity]=n
this will give you the desired results.
This question is more related to MySQL query. But you can achieve it by javascript as follows:
Product.find({}, fuction(err, products) {
if(err) throw err;
//considering products as array of product. Otherwise you can get to depth for array of product.
var filteredProducts = products.filter(function(p1) {
return p1.Quantity === p1.SelledQuantity;
});
//Your desired output
console.log(filteredProducts);
});
This will be slow but will work for smaller database size. For more optimized answer, ask the question in mysql section with respect to database and table structure.

Query MongoDB for documents with non-empty intersection of arrays

So - I have a Heroku + Node + Express + MongoDB app in development, and I've been playing around all day trying to figure out how to get the mongo query I want. In a mongo collection, every document has a property 'fruits', which is an array of strings like
fruits : ['apple', 'orange', 'lemon']
The size and contents of fruits is arbitrary. I want to query the database for all documents whose fruits array has at least one element in common with another array which I provide when I make the query. How can I achieve this?
What I've tried so far centers around the $where query and some server-side JS. If I do something like
mongo.Db.connect(mongoUri, function(err, db) {
db.collection('Users', function(er, collection) {
collection.find( {$where: 'false' } ).toArray(function(err, matches){
console.log(matches)
...
});
});
});
I get an empty array - good! And if I change the false to true, I get the whole DB - also good! Things get less good with something like the following:
collection.find( {$where: function(){return false} } ).toArray(function(err, matches){...
I get the whole DB again! I thought this would pretty obviously return the same empty array as the very first example I gave above, as per the syntax presented at http://docs.mongodb.org/manual/reference/operator/query/where/
I was trying the above out just to see if I could get anything to work with $where, but further problems arise when I try to add server-side JS to the mix. I had a look at
http://docs.mongodb.org/manual/tutorial/store-javascript-function-on-server/
and added the following to the system.js collection in my DB:
{
"_id": "isMatch",
"value": "function(){return false}"
}
and modified the above example with the line
collection.find( {$where: 'isMatch()' } ).toArray(function(err, matches){...
thinking that isMatch would now be in mongo's bag of tricks when it eval's the string (I assume it must be eval'ing the string), and the string syntax was the best bet from the playings around described above. Sadly this results in a console log of null - another flavour of wrong, I expected again an empty array.
Can anyone help me achieve the query I described above? I suspect I can work out the nitty gritty myself if you kind folks can help me understand 1. How to use the function syntax instead of the string syntax for $where, and 2. How to correctly declare a JS function in system.js for use with $where. Thanks in advance!
Your $where queries work fine when I try them in the shell, but you don't need to use $where for this query. Instead, you can use an $in that targets your fruits field to find docs that contain at least one matching fruit:
var fruits = ['apple', 'orange', 'lemon'];
db.test.find({fruits: {$in: fruits}});
Don't quite seem to know what was the case in 2013, but now I think you have to use $elemMatch along with $in that targets your fruits field to find docs that contain at least one matching fruit:
var fruits = ['apple', 'orange', 'lemon'];
db.test.find({fruits: {$elemMatch: {$in: fruits}}});

sorting alpha with mongoose

I'm trying to sort via mongoose 3.6.20 and I am receiving some unexpected results.
I have a list of companies with a name. At first I thought that maybe it was sorting in a case sensitive way. Which based on articles, I expect was true.
I'm now using a virtual property to down case the sort field. However, I'm still getting unexpected results.
CompanySchema.virtual('name_lower').get(function(){
return this.name.toLowerCase();
});
and when I sort
Company.find().sort({ name_lower: 1 });
I'm getting it in the following order:
company name
google
company name (yes a duplicate for testing)
I'm also outputting the value of my virtual property and it looks right. There is no whitespace or funky characters that would result in the 2nd 'company name' from appearing after google.
Using nodejs, express, mongoose.
What am I missing or doing incorrectly?
Update:
Based on the information provided in the answers, I refactored my schema to include some normalized fields and hooked into the pre save event of my document, where I update those normalized fields and sort using them in all future queries.
CompanySchema.pre('save', function(next){
this.normalized_name = this.name;
});
Next, is in the schema I use:
var CompanySchema = mongoose.Schema({
...
normalized_name: { type: String, set: normalize },
...
});
Where normalize is a function that for now, returns a lowercase version of the value passed into it. However, this allows me to expand on it later really fast, and I can quickly do the same to other fields that I might need to sort against.
As of MongoDB v3.4, case insensitive sorting can be done using collation method:
Company.find()
.collation({locale: "en" }) //or whatever collation you want
.sort({name:'asc'})
.exec(function(err, results) {
// use your case insensitive sorted results
});
Unfortunately MongoDB and Mongoose does not currently support complex sorting, so there are 2 options:
As you said, create a new field with the names sanitized to be all lowercase
Run a big for loop over all the data and update each company name to it's lower case form:
db.CompanyCollection.find().forEach(
function(e) {
e.CompanyName = e.CompanyName.toLowerCase();
db.CompanyCollection.save(e);
}
)
or
db.CompanyCollection.update({_id: e._id}, {$set: {CompanyName: e.CompanyName.toLowerCase()
Please see Update MongoDB collection using $toLower and Mongoose: Sort alphabetically as well for more info.
I want to put out that in this hook:
CompanySchema.pre('save', function(next){
this.normalized_name = this.name;
});
You'll have to call next(); at the end, if you want the normalized_name to be saved in the database, so the pre save hook would look like:
CompanySchema.pre('save', function(next){
this.normalized_name = this.name;
next();
});
This answer seems to be more helpful to me. I had to consider diacritics along with the case so I had used strength:3.
Mongoose: Sort alphabetically

Resources