text index required for $text query in the MongoDB with Express - mongodb

I built a Express back-end which is connected to local MongoDB
when I create a post to find out the specific term, it returns the error like this
MongoError: text index required for $text query
How can I fix this error?
this is my Express code
app.post("/api/search",(req,res)=> {
const term = req.body.searchTerm;
console.log(term); // Here I can check the request coming well
MongoClient.connect(url,(err,db)=> {
if(err) throw err;
const dbo = db.db("Exercise");
dbo.collection("exercise")
.find({$text:{$search:term}})
.toArray((error, result) => {
if (error) {
console.log(error);
return;
} else {
console.log("Success");
res.send(result);
}
});
});
});
Thank you in advance!

What you can do to solve your problem is simple. You can create a "text" on the field that you want search in you collection like this:
db.reviews.createIndex( { comments: "text" } )
Note: assumes "comments" is the field as seen in the docs
However, for the best results you might want to create a dynamic default Search index in MongoDB Atlas. It's free and easy. Because it is a dynamic default, you do not need to do any configuration. The results and performance will likely be better if you are powering a search experience.
I hope this info helps.

Related

mongoose query return empty array if filter has a field name that is camel cased

The following issue was with some code written in express/nodejs.
I experienced something I could not find any material in regards,
I encountered an issue whereas making a find query with mongoose returned an empty array.
After tinkering I discovered it was due to the key used (customerRef) being camel cased
I tried to tweak other fields from and to camelcase and got the same issue.
see example here:
const lastCart = await Cart.findOne({ customerRef: customer._id }, function(err, docs) {
if (err) {
console.log('TCL: err', err);
}
console.log('TCL: docs', docs); //result = []
});
then went and tweaked my database objects from:
{
....
"customerRef" : "5dfcd194ca19972b888d66c2",
....
}
to:
{
....
"customerref" : "5dfcd194ca19972b888d66c2",
....
}
then the query worked as expected:(customerRef >>> customerref)
const lastCart = await Cart.findOne({ customerref: customer._id }, function(err, docs) {
if (err) {
console.log('TCL: err', err);
}
console.log('TCL: docs', docs); //result is now ok = [...]
});
Now to top this off here's another bit of info:
I use studio 3T for mMongoDB and over there I do not have this issue when making queries.
I would appreciate any and all explanation on this behavior aswell as alternatives if possible.
See my full repo here:
(My skills are novice at best so I apologize in advanced and welcome any feedback)
https://github.com/Darkmift/MEAN-SuperMarket/blob/master/backend/app/controllers/userController.js#L121
So after some time I have concluded the issue was with my use of the mongo shell program (Studio 3t)
at the time I was not aware how to properly insert an ObjectId.
When using the built in query one must add:
{
...
"customerRef" : ObjectId(RefId)
...
}
and NOT:
{
...
"customerRef" : "RefId"
...
}

Mongoose - populate return _id only instead of a Object [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'}])

How to properly instantiate a Waterline Model Object from a sails-mongo native result?

I am using SailsJS on a project and I need to use native() for certain querys. The problem I have is that I can't find a proper way to instantiate a Waterline Model Object from the mongo collection find result. I have being searching information about this and the only thing I have found is the following:
var instance = new Model._model(mongo_result_item);
This should work properly, but when I do instance.save(function(err, ins){}); the model throws an error because of the "_id" field, that should be "id".
I have took a look into sails-mongo code and I found that for the "find" method they do this:
// Run Normal Query on collection
collection.find(where, query.select, queryOptions).toArray(function(err, docs) {
if(err) return cb(err);
cb(null, utils.normalizeResults(docs, self.schema));
});
So the normalizeResults does the magic with the "_id" attribute, and other stuff.
The way I am doing this right now is to require the sails-mongo utils.js file to have access to this method.
Full sample:
var mongoUtils = require('sails-mongo/lib/utils.js');
SampleModel.native(function(nativeErr, collection){
collection.find({ 'field' : value }).toArray(function(collectionErr, results){
if (!results || results.length == 0) return res.restfullInvalidFieldValue({ msg : 'INVALID_VALUE' });
var norm_results = mongoUtils.normalizeResults(results);
var instance = new SampleModel._model(norm_results[0]);
});
});
Is there a better / proper way to achieve this ?
I need to do a native search because I have found a problem with Waterline find() method using strings, where the search should be case sensitive. Every string field on the model is being used as a regular expression match of the form : /^{string}$/i
Searching by a regular expression with the case insensitive flag will give me problems. In the other hand, doing { field : { $regex : new RegExp('^'+regexp_escaped_string+'$') } } could be possible, but I think it will perform worst than { field : value }.
If someone have found a different workaround for the case insensitive problem, please, point me in the right direction.
Thanks in advance.
$regex might help you to search case insensitive string using option paramteter as "i", you can also specify custom regex instead for more information see $regex mongodb documentation.
/**
* PetController
*
* #description :: Server-side logic for managing pets
* #help :: See http://links.sailsjs.org/docs/controllers
*/
module.exports = {
searchByName: function (req,res) {
Pet
.native(function(err, collection) {
if (err) return res.serverError(err);
collection.find(
{
name: { $regex: /like-my-name/, $options: "i" } // here option "i" defines case insensitive
},
{
name: true
})
.toArray(function (err, results) {
if (err) return res.serverError(err);
return res.ok(results);
});
});
}
};
See here for more on native mongo query - https://stackoverflow.com/a/54830760/1828637

WaterlineJs find() with no criteria and fields/select provided does not work

I am trying to fetch all the records but with selected fields, I have tried the following ways but none works:
Post.find(
{
where: {},
select: ['title']
}
);
Post.find(
{},
{
fields: {
title: 1
}
}
);
As this answer points out, the fields param "WILL work as long as you pass other params with it such as limit or order."
Alternatively, if you want this throughout your application, you could define a custom toJSON function for your model, under attributes. If not, you could still define it under some other (e.g. filter) and use map to return the custom objects instead of the default model. Remember to take care of the control flow while using map though. Use async/promises/raw logic to avoid returning before all objects are processed.
The issue has been resolved in sails-mongo latest version:
https://github.com/balderdashy/waterline/issues/1098
Thanks
I've played with trying to get above answer to use limit or order to kick in the projection to no avail.
I did see this in the docs located here:
http://sailsjs.org/documentation/reference/waterline-orm/models/native
With an out of the box solution for exactly what you're doing (pasted here for ease of use).
Pet.native(function(err, collection) {
if (err) return res.serverError(err);
collection.find({}, {
name: true
}).toArray(function (err, results) {
if (err) return res.serverError(err);
return res.ok(results);
});
});
Swap out the response base things and change Pet to Post and, this ought to work in the sails console:
Post.native(function(err, collection) {
if (err) throw new Error(err);
collection.find({}, {
title: true
}).toArray(function (err, results) {
if (err) throw new Error(err);
console.log(results);
});
});
You'll still get the _id field, and if you don't want that then hit the Mongo docs on not getting those hint(title: true, _id: false)hint
Hope this helps!

Weird Mongoose. I get crap from findOne's doc.

Here is what my script look like:
UserModel.findOne(
{userId:'xx123456'},
function(err, doc) {
if (err) {
next(err);
} else if (! doc) {
next( 'findOne: User not found. Input: ' + JSON.stringify(data)));
} else {
next(null, {status:true,
message:'User is found' ,
data:doc
});
}
}
);
When I run unit test in command line, doc is the mongo Document. e.g. {userId:'xx123456', name:"My Name' }
However, when I make a web app calling this function passing exactly the same userId, doc.userId is empty. However, I can find the data in doc._doc why is that?
I have another site that return the full document from "doc" normally. Why there is inconsistency...
I have to do this to normalize the data: if (docs) docs = JSON.parse(JSON.stringify(docs));
Not really sure what's happening, but I'd first try a UserModel.find({}); Do this to see what, if anything, returns. If nothing returns you have a problem.
Also, one issue I've seen is that Mongoose is case sensitive. Make sure 'userId' is not supposed to be something like 'userid.' Command line will work (as far as I've seen) with the wrong case. Mongoose / Node will not.