49

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.

Undo
  • 25,519
  • 37
  • 106
  • 129
kmehta
  • 2,457
  • 6
  • 31
  • 37
  • 5
    I would really appreciate a solution which doesn't kill word stemming. – Mitja Oct 29 '14 at 00:55
  • 2
    Could you find solution with word stemming? – Zhomart Dec 04 '14 at 03:28
  • There's a new and much better way to too accomplish this in MongoDB Atlas using Lucene: https://docs.atlas.mongodb.com/atlas-search It supports stemming and autocomplete. – Nice-Guy Dec 08 '20 at 03:05

4 Answers4

85

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").

starball
  • 20,030
  • 7
  • 43
  • 238
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • 14
    Note that this throws cool features of the text index like word stemming into the trash whatsoever. If my text is `These are some cool test entries here` , I will find it with `{$search:"entry"}` but not with `{$search:"\"entry\""}`. – Mitja Oct 29 '14 at 00:54
  • 2
    @Mitja Any way to do this without losing word stemming? – tim-phillips Jun 19 '16 at 02:06
  • 4
    Here's an example in JS from a series of words, and with index creation, if that helps : `var queryString = '\"' + q.split(' ').join('\" \"') + '\"'; var res = mycollection.find({ $text: { $search: queryString, $language: "fr" } });` and in mongo `db.mycollection.createIndex( { "fieldName": "text"} )` – Antoine F. Sep 26 '16 at 15:01
  • 2
    IMPORTANT NOTICE: works only with double quotes! With single quotes acts like standard $text search. – avalanche1 Oct 21 '16 at 21:43
  • What if my text fields have a double-quote included, for instance when talking about appliances or whatnot: Kitchenaid KBSN608EPA 48" Refrigerator and I want the user to be able to find '48"' in the product title (because they WILL and DO use " to denote inches) – Ticdoc Feb 26 '19 at 21:35
  • 1
    It seems there is some issue with monogdb while searching multiple phrases. I have field having value `456 ABC`. When I do multiple phrase search with keyword `{ "$search" : "\"45\" \"ABC\"" }` or `{ "$search" : "\"456\" \"AB\"" }`, It returns document. It shouldn't return result as both phrases are not exactly matched – Mahesh Bhuva Apr 25 '19 at 06:37
  • 1
    With MongoDB 4.2 at least, it appears that stemming is actually maintained when performing a logical AND search using phrases for each term. For example, searching for \"Deconstruct\" returns results like "Deconstructed Pant". Not only that, but searching for something like \"Deconstruct\" \"Sh\" also narrows down results further to return only results that also contain "Sh", like "Deconstructed Shirt". Searching for \"Sh\" on its own however without another helper term returns no results. – Roman Scher Dec 12 '19 at 22:45
  • 1
    this is totally wrong. according to document: If the $search string includes a phrase and individual terms, text search will *only* match the documents that *include* the phrase. – Wang Jul 08 '21 at 16:51
6

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.

chovy
  • 72,281
  • 52
  • 227
  • 295
1

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(' ')})
Abhay Verma
  • 133
  • 1
  • 8
-1

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)
Black Mamba
  • 13,632
  • 6
  • 82
  • 105