65

As I find out, since version 3.8.9, mongoose support full text search. But I can't find a good documentation for it!
I want to do something like:

db.collection.ensureIndex(
    // Fields to index
    {
        animal:  "text",
        color:   "text",
        pattern: "text",
        size:    "text"
    },

    // Options
    {
        name: "best_match_index",

        // Adjust field weights (default is 1)
        weights: {
            animal: 5,  // Most relevant search field
            size:   4   // Also relevant
       }
    }
)

Can I do it with pure mongoose? Or I have to use some plugin like mongoose-text-search? How about without weight?
And how should I do it?

Machavity
  • 30,841
  • 27
  • 92
  • 100
Foad Nosrati Habibi
  • 1,134
  • 1
  • 8
  • 13
  • Have you tried calling [`index`](http://mongoosejs.com/docs/api.html#schema_Schema-index) on the schema to add the text index? – JohnnyHK Jul 12 '14 at 15:41
  • I did it in schema as index: 'text', but I want to index on multiple fields as above example. – Foad Nosrati Habibi Jul 12 '14 at 16:26
  • Don't declare it in your schema definition, _call_ the `index` method of your schema. http://mongoosejs.com/docs/api.html#schema_Schema-index – JohnnyHK Jul 12 '14 at 20:07
  • you mean 'schema.index({ animal: "text", color: "text", pattern: "text", size: "text" })' ? What about weight? And does it make single index or 4 indexes? – Foad Nosrati Habibi Jul 12 '14 at 20:21

4 Answers4

154

Yes, you can use full text search in Mongoose >= 3.8.9. Firstly, a collection can have at most one text index (see docs). So, to define text index for several fields, you need compound index:

schema.index({ animal: 'text', color: 'text', pattern: 'text', size: 'text' });

Now you can use $text query operator like this:

Model
    .find(
        { $text : { $search : "text to look for" } }, 
        { score : { $meta: "textScore" } }
    )
    .sort({ score : { $meta : 'textScore' } })
    .exec(function(err, results) {
        // callback
    });

This will also sort results by relevance score.

As for weights, you can try to pass weights options object to index() method (where you define compound index) (working at least with v4.0.1 of mongoose):

schema.index({ animal: 'text', color: 'text', pattern: 'text', size: 'text' }, {name: 'My text index', weights: {animal: 10, color: 4, pattern: 2, size: 1}});
Anthony O.
  • 22,041
  • 18
  • 107
  • 163
eagor
  • 9,150
  • 8
  • 47
  • 50
  • 5
    returned error: need exactly one text index for $text query – Mallen Sep 29 '14 at 17:41
  • 1
    to define text index for several fields, you need compound index. Make sure you're defining compound index right. – eagor Sep 30 '14 at 12:45
  • 3
    Is this procedure documented somewhere? I'm trying to use text index with mongoose but it doesn't work. I have created a compound index, I have used the $text operator like your example, but the result is always an empty document. – ira Jan 04 '15 at 08:49
  • 2
    see [$text doc](http://docs.mongodb.org/manual/reference/operator/query/text/#op._S_text) and [text search tutorials](http://docs.mongodb.org/manual/administration/indexes-text/). What is your Mongoose version? (full text search works for ver >= 3.8.9) – eagor Jan 04 '15 at 11:27
  • Those doc links are for Mongo. Is there anything for Mongoose itself? – Michael Cole Jun 11 '15 at 20:34
  • With version 4.0.1 of mongoose, the weights options were operationnal. – Anthony O. Sep 10 '15 at 13:31
  • 2
    @Mallen did you find a solution? – OMGPOP Dec 07 '15 at 15:04
  • I'm trying to access the score property after I get the results in order to display them on my app, but for some reason I'm getting undefined. If I console.log the results array, I do see the score property under each object, but if I specifically try to access it, I get undefined. Any ideas why or how to correctly do this? thank you. – Alejandro Corredor Jun 15 '17 at 20:43
  • Thank you this works perfectly for creating a index when using mongoose. Can you please let me know how to drop the index in mongoose ? – Goutham Jun 22 '17 at 05:12
2

As of MongoDB 2.6, a collection can have at most one text index (documented here). Therefore, you will not be able to do what you want with the current version of MongoDB. Really, for complicated text searching problems with requirements of different weights depending on the location of the match, you should consider a full-blown text searching solution like Solr or ElasticSearch.

As a workaround in MongoDB, you could tokenize the fields manually, store them as keyword arrays, and index them:

animal: ["The", "quick", "brown", "fox", "jump", ..., "dog"]

then a query like

db.test.find({animal: {$in: ["brown", "shoes"]})

mimics text search. There are a few limitations of this approach like the manual work required to set it up, the fact that there will be no stemming to, e.g., match "dreaming" with "dream", the fact that stopwords will not be removed like in a normal text index, and the absence of any weighting mechanism.

wdberkeley
  • 11,531
  • 1
  • 28
  • 23
  • 1
    In fact I have just one text index with multiple fields. The blow code is part of .getIndexes(). I did it with [mongoose-text-search](https://www.npmjs.org/package/mongoose-text-search), I'm looking for a pure mongoose version of it. ` { "v" : 1, "key" : { "_fts" : "text", "_ftsx" : 1 }, "ns" : "public-diary-dev.diaries", "name" : "full-search-index", "background" : true, "weights" : { "tags" : 1, "text" : 1, "title" : 1 }, "default_language" : "english", "language_override" : "language", "textIndexVersion" : 1 } ` – Foad Nosrati Habibi Jul 27 '14 at 07:43
1

I found the following article that led me to the http://code.tutsplus.com/tutorials/full-text-search-in-mongodb--cms-24835 I dropped the index created in the top answer using the following

db.tablename.dropIndex({"indexname_text"})  

I got the list of indexes with this command

db.tablename.getIndexes()

I then used the following to create the indexes

db.tablename.createIndex({"$**":"text"})

the following commands worked in Mongoose

model.find(
    {$text: {$search: "text you are searching for"}},
    {score: {$meta: "textScore"}})
    .sort({score:{$meta:"textScore"}}
)
.exec(function(err, results) {
    `enter code here`if(!err){
    console.log('results ' + results);
}
else
{
    console.log(err);
}
});
-4
    var searchQuery=new RegExp('dam', 'i');
    var query = { firstName : searchQuery };
    Model.find(query ...
dam1
  • 3,536
  • 3
  • 22
  • 18
  • This answer is the bomb! I wasted the last two hours trying to add a text index to my schema and this simple-as-f**k answer is all I needed. Huzzah! – ecg8 Feb 24 '18 at 03:57
  • Yes, It is, but you can't create human-friendly query avoiding use the regular expressions. – Akhmedzianov Danilian May 13 '18 at 11:45
  • Careful with this, as it could accidentally result in [regex denial of service vulnerability](https://medium.com/@liran.tal/node-js-pitfalls-how-a-regex-can-bring-your-system-down-cbf1dc6c4e02) – BrotherDonkey Jun 08 '20 at 02:24
  • Fully agreed with BrotherDonkey ...Please post anwers with taking security in mind ! – Sanmeet Nov 01 '21 at 18:18