48
votes

I have an index on an array "keys" that I am using to provide full text functionality to my applicaiton.

With the release of 2.4.3, I'd like to utilize the "text" index type. I insured a "text" index type on my array "keys" and it seems to work SUPER fast (faster than my old keywords full text method).

The problem is, my app assumes that fields are inclusive (AND). By default, the text search ORs my parameters.

Does anyone know of a way to run a text search inclusively?

For example:

db.supplies.runCommand("text", {search:"printer ink"})

should return results with both printer and ink, instead of all results with either printer or ink.

4
I would really appreciate a solution which doesn't kill word stemming. - Mitja
Could you find solution with word stemming? - Zhomart
There's a new and much better way to too accomplish this in MongoDB Atlas using Lucene: docs.atlas.mongodb.com/atlas-search It supports stemming and autocomplete. - Nice-Guy

4 Answers

81
votes

Give a try to:

db.supplies.runCommand("text", {search:"\"printer\" \"ink\""})

Also, here's a quote from docs:

If the search string includes phrases, the search performs an AND with any other terms in the search string; e.g. search for "\"twinkle twinkle\" little star" searches for "twinkle twinkle" and ("little" or "star").

Hope that helps.

5
votes

You can wrap each word in double-quotes:

let keywords = ctx.params.query.split(/\s+/).map(kw => `"${kw}"`).join(' ');
match.$text = { $search: keywords, $caseSensitive: false };

There is a downside if the user inputs a quoted string this will not work. You'd have to parse out quoted strings first.

1
votes

As @alecxe pointed out earlier, to do AND search on text index column you need to double quote each search word. Below is a quick one-liner for your requirement.

db.supplies.runCommand("text", {search: "printer ink".split(" ").map(str => "\""+str+"\"").join(' ')})
-1
votes

Here is a simple function I made to search using subwords in node. Hope it helps someone

Let's suppose a user search for pri nks so it should satisfy printer and inks but $text search doesn't allow for this so here is my simple function:

    var makeTextFilter = (text) => {
    var wordSplited = text.split(/\s+/);
    /** Regex generation for words */
    var regToMatch = new RegExp(wordSplited.join("|"), 'gi');
    let filter = [];
    searchFieldArray.map((item,i) => {
        filter.push({}); 
        filter[i][item] = {
            $regex: regToMatch,
            $options: 'i'
        }
    })

    return filter;
  }

and use it in your query like this

let query = {...query, $or: makeTextFilter(textInputFromUser)}
tableName.find(query, function (err, cargo_list)