1
Model.find({ $text : {$search: "#text"} })

returns everything that includes "text", not only those documents with "#text". I've tried putting an \ before the #, to no avail. How do I stop mongodb from ignoring my special characters? Thanks.

user36322
  • 241
  • 3
  • 13
  • possible duplicate of [How to query mongodb with "like"?](http://stackoverflow.com/questions/3305561/how-to-query-mongodb-with-like) – Tomalak Feb 07 '15 at 08:50
  • 2
    `$text` is a full text search. It does not work for exact sub-string matches, it searches word forms and phrases, just like a search engine. Use are regex for exact matches. – Tomalak Feb 07 '15 at 08:54
  • If I search for "xtext" then the document with "text" doesn't show up, why is it different for "#text"? – user36322 Feb 07 '15 at 10:50
  • 1
    Because, as I said, `$text` is a *full text search*. It breaks up a string into words and stores those words (and *word forms*, so that `'text'` and `'texts'` are the same thing) in an index. You can then search through that index. `'text'` and `'xtext'` are end up as separate words. `'#text'` on the other hand will be broken up as `'text'`, because a full text index ignores symbols. – Tomalak Feb 07 '15 at 10:55

1 Answers1

3

Tomalak's description of how text indexing works is correct, but you can actually use a text index for an exact phrase match on a phrase with a special character:

> db.test.drop()
> db.test.insert({ "_id" : 0, "t" : "hey look at all this #text" })
> db.test.insert({ "_id" : 1, "t" : "text is the best" })
> db.test.ensureIndex({ "t" : "text" })

> db.test.count({ "$text" : { "$search" : "text" } })
2
> db.test.count({ "$text" : { "$search" : "#text" } })
2

> db.test.find({ "$text" : { "$search" : "\"#text\"" } })
{ "_id" : 0, "t" : "hey look at all this #text" }

Exact phrase matches are indicated by surrounding the phrase in double quotes, which need to be escaped in the shell like "\"#text\"".

Text indexes are larger than normal indexes, but if you are doing a lot of case-insensitive exact phrase matches then they can be a better option than a standard index because they will perform better. For example, on a field t with an index { "t" : 1 }, an exact match regex

> db.test.find({ "t" : /#text/ })

performs a full index scan. The analogous (but not equivalent) text query

> db.test.find({ "$text" : { "$search" : "\"#text\"" } })

will use the text index to locate documents containing the term "text", then scan all those documents to see if they contain the full phrase "#text".

Be careful because text indexes aren't case sensitive. Continuing the example above:

> db.test.insert({ "_id" : 2, "t" : "Never seen so much #TEXT" })

> db.test.find({ "t" : /#text/ })
{ "_id" : 0, "t" : "hey look at all this #text" }

> db.test.find({ "$text" : { "$search" : "\"#text\"" } })
{ "_id" : 0, "t" : "hey look at all this #text" }
{ "_id" : 2, "t" : "Never seen so much #TEXT" }
wdberkeley
  • 11,531
  • 1
  • 28
  • 23
  • Nice, I didn't think that `$text` could be used that way. Good to know. – Tomalak Feb 08 '15 at 00:56
  • If there a way to do multiple search phrases? Searching for 'text othertext' would return documents that contain either text OR othertext, is this possible with exact search phrases? – user36322 Feb 12 '15 at 01:23