mongoDB - Text Search in multiple fields using node.js - node.js

I am trying to using Node.JS connected to a MongoDB database (all hosted on heroku, so using MongoLab plugin) to perform a text search. I would like to search certain fields (strings or arrays of strings, however I can change them to all strings need be) of my documents for a variable keyword.
What the code below hopefully would do is search either the 'title' field or 'ingredients' field for the keyword variable, and then return those results.
I am dubious of the ensureIndex lines (tried both ensureIndex and createIndex) because removing them does not change the functionality of the program.
Any help would be appreciated!
app.get('/searchtitle', function(request, response) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
response.set('Content-Type', 'application/json');
var type = request.query.type;
var keyword = request.query.keyword;
if (type == 'title') {
db.collection('Test_Recipes').ensureIndex( { title: "text" } );
db.collection('Test_Recipes').find({
$text: {$search: keyword }
}).toArray(function(err, results){
response.send(JSON.stringify(results)) });
}
else {
console.log("in else case: type is "
+ type + " and keyword is " +keyword);
db.collection('Test_Recipes').ensureIndex({ingredients :"text"});
db.collection('Test_Recipes').find({
ingredients: {$elemMatch: keyword } })
.toArray(function(err, results){
response.send(JSON.stringify(results)) });
}
}

Indexes, as in any databases, need to be created only once, when the collection is first created. Index creation is a costly operation and a blocking operation.
As of mongoDB 3.0 there is no difference between the methods createIndex() and ensureIndex(), and either of them should be used to create indexes on the collection, only once, in the server side, when the collection is created and be modified only when required.
To index both the title and the ingredients fields, you need to create index on the collection as:
db.collection.createIndex({"ingredients":"text","title":"text"})
This would ensure that both the fields are indexed, when the document is inserted.
I am dubious of the ensureIndex lines (tried both ensureIndex and
createIndex) because removing them does not change the functionality
of the program. Any help would be appreciated!
This is because, how the createIndex() operation behaves. Only the first call to this method succeeds and others are simply ignored, if the indexes are created on the same field(s) again.
Then just querying, as below would query for the keyword in both the title and ingredients fields.
var type = request.query.type;
var keyword = request.query.keyword;
db.collection('Test_Recipes').find({
$text: {$search: keyword }
}).toArray(function(err, results){
response.send(JSON.stringify(results))
});

Related

Skip fields on collection.find() not working

I'm developing a report system in Node.JS and MongoDB
I'm trying to make a query to get some datas from the MongoDB, but the query don't specify all the fields that the collection have
(example, in the query I'm asking for the field typ, event and date, but in the collection I have more than 10 fields).
db.collection('logs').find({
system: {
type: query.logType,
event: query.event,
date: query.date
}
}).toArray(function (err, document) {
if (err) throw err;
console.log(document);
});
This way, it doesn't return anything, but when I specify all the fields that the collection have, the query works.
Is there any way to use RegEx or skip the other not-needed fields on the query?
you can use map and select properties in that function
let tmp = [{id:1,name:"aaa"},{id:2,name:"bbb"}].map((item)=>{
// your can make new object here
return {name: item.name};
});
console.log(tmp);

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)

Why can't I seem to merge a normal Object into a Mongo Document?

I have a data feed from a 3rd party server that I am pulling in and converting to JSON. The data feed will never have my mongoDB's auto-generated _ids in it, but there is a unique identifier called vehicle_id.
The function below is what is handling taking the data-feed generated json object fresh_event_obj and copying its values into a mongo document if there is a mongo document with the same vehicle_id.
function update_vehicle(fresh_event_obj) {
console.log("Updating Vehicle " + fresh_event_obj.vehicleID + "...");
Vehicle.find({ vehicleID: fresh_event_obj.vehicleID }, function (err, event_obj) {
if (err) {
handle_error(err);
} else {
var updated = _.merge(event_obj[0], fresh_event_obj);
updated.save(function (err) {
if (err) {
handle_error(err)
} else {
console.log("Vehicle Updated");
}
});
}
});
}
The structures of event_obj[0] and fresh_event_obj are identical, except that event_obj[0] has _id and __v while the "normal" object doesn't.
When I run _.merge on these two, or even my own recursive function that just copies values from the latter to the former, nothing in the updated object is different from the event_obj[0], despite fresh_event_obj having all new values.
Does anyone have any idea what I'm doing wrong? I feel it is obvious and I'm just failing to see it.
The problem is that if you don't have properties defined in your schema, and if they don't already exist, you can't create them with
doc.prop = value
even if you have {strict:false} in your schema.
The only way to set new properties is to do
doc.set('prop', value)
(You still have to have {strict:false} in your schema if that property doesn't exist in your schema)
As for having too many properties to be defined in schema, you can always use for-in loop to go through object properties
for(key in fresh_event_obj)
event_obj.set(key, fresh_event_obj[key]);

Using the find method on a MongoDB collection with Monk

I am working through a MEAN stack tutorial. It contains the following code as a route in index.js. The name of my Mongo collection is brandcollection.
/* GET Brand Complaints page. */
router.get('/brands', function(req, res) {
var db = req.db;
var collection = db.get('brandcollection');
collection.find({},{},function(e,docs){
res.render('brands', {
"brands" : docs
});
});
});
I would like to modify this code but I don't fully understand how the .find method is being invoked. Specifically, I have the following questions:
What objects are being passed to function(e, docs) as its arguments?
Is function(e, docs) part of the MongoDB syntax? I have looked at the docs on Mongo CRUD operations and couldn't find a reference to it. And it seems like the standard syntax for a Mongo .find operation is collection.find({},{}).someCursorLimit(). I have not seen a reference to a third parameter in the .find operation, so why is one allowed here?
If function(e, docs) is not a MongoDB operation, is it part of the Monk API?
It is clear from the tutorial that this block of code returns all of the documents in the collection and places them in an object as an attribute called "brands." However, what role specifically does function(e, docs) play in that process?
Any clarification would be much appreciated!
The first parameter is the query.
The second parameter(which is optional) is the projection i.e if you want to restrict the contents of the matched documents
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 },function(e,docs){})
would mean to get only the item and qty fields in the matched documents
The third parameter is the callback function which is called after the query is complete. function(e, docs) is the mongodb driver for node.js syntax. The 1st parameter e is the error. docs is the array of matched documents. If an error occurs it is given in e. If the query is successful the matched documents are given in the 2nd parameter docs(the name can be anything you want).
The cursor has various methods which can be used to manipulate the matched documents before mongoDB returns them.
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 })
is a cursor you can do various operations on it.
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 }).skip(10).limit(5).toArray(function(e,docs){
...
})
meaning you will skip the first 10 matched documents and then return a maximum of 5 documents.
All this stuff is given in the docs. I think it's better to use mongoose instead of the native driver because of the features and the popularity.

Query Return Collection From MongoDb Mongoose in Node

Is it possible to query a returned collection in mongoose for mongodb. So in the example below I wish to return to the page the full list of steps, as well as the currentStep. The line I am unsure about is currentStep: steps.?????(page).
Thanks
var page = (req.params.page == undefined?1:req.params.page)
db.stepModel.find().sort({number:1}).exec(function(err, steps) {
if (err) { return next(err); }
res.render('scheme', {
title: 'Fnol',
user: req.user,
steps:steps,
currentStep: steps.?????(page),
page:page
});
};
You can use steps as an array:
currentStep : steps[page - 1].toObject()
The - 1 is because it seems that you use 1-based indexing for the page, whereas arrays are 0-based.
The toObject() converts the Mongoose result into a proper JS object, so you can access properties like _id from within your template.

Resources