620

I'm looking for an operator, which allows me to check, if the value of a field contains a certain string.

Something like:

db.users.findOne({$contains:{"username":"son"}})

Is that possible?

Bjorn
  • 64,873
  • 39
  • 133
  • 161
johnny
  • 7,684
  • 6
  • 24
  • 35

15 Answers15

972

You can do it with the following code.

db.users.findOne({"username" : {$regex : "son"}});
Skippy le Grand Gourou
  • 5,996
  • 4
  • 52
  • 70
Parvin Gasimzade
  • 23,335
  • 8
  • 54
  • 80
  • 22
    Note that this will **not** make efficient use of an index and result in all values being scanned for matches. See the notes on [Regular Expressions](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-RegularExpressions) – Stennie Jul 18 '12 at 20:54
  • 8
    @Stennie, then what do you suggest to make efficient use of index and find a substring. – Blue Sky Nov 07 '12 at 19:31
  • 4
    @Vish: if your common use case is free-text searching of a field and you have a large number of documents, I would tokenize the text for more efficient queries. You could use [multikeys](http://www.mongodb.org/display/DOCS/Full+Text+Search+in+Mongo) for a simple full-text search, or perhaps build an [inverted index](http://en.wikipedia.org/wiki/Inverted_index) as a separate collection. For infrequent searches or a small collection of documents, scanning the full index may be acceptable (though not optimal) performance. – Stennie Nov 08 '12 at 01:57
  • 120
    Isn't this a bit of an overkill? What you want is ``db.users.findOne({"username" : {$regex : "son"}});`` – JamieJag Mar 21 '14 at 12:46
  • 3
    Might want to check out full text search in Mongo 2.6 – wprl Sep 05 '14 at 18:38
  • How we can use above in Spring Data Mongo Repository method? – PAA Sep 22 '15 at 11:59
  • Could anyone please reply on above query ? –  Oct 11 '15 at 21:12
  • in Spring, try Query query = new Query(); query.addCriteria(Criteria.where("username").regex(".*son.*")); – Rondo Oct 24 '16 at 22:08
  • @Stennie , Is that statement still true in 3.4? https://docs.mongodb.com/manual/reference/operator/query/regex/#pcre-vs-javascript – mjwrazor Mar 17 '17 at 15:40
  • 1
    @mjwrazor Still applicable to MongoDB 3.4 as per the `$regex` documentation page you linked and the information on [Index Use](https://docs.mongodb.com/manual/reference/operator/query/regex/#index-use). The best case regex search would be case-sensitive prefix scan; searches for non-prefix substrings or with case-insensitive options are not going to result in effective index usage. This is probably fine for small indexes, but extensive use of non-prefix regexes will affect performance for queries against larger indexes. – Stennie Mar 19 '17 at 04:51
  • @mjwrazor The underlying index format is currently [B-tree](https://www.cs.usfca.edu/~galles/visualization/BTree.html) which doesn't lend itself to arbitrary substring matches. A [Text index](https://docs.mongodb.com/manual/core/index-text/) might be a better option if you are doing language-based search (eg. where text can be split on word boundaries and optionally stemmed), but for other use cases (eg. fuzzy matching or free text search) and larger data sets you can potentially work out a more efficient schema or tokenising approach. – Stennie Mar 19 '17 at 04:53
  • @Stennie so if I use the $regex with case-sensitive prefix I will be utilizing the index if i use the regex `.*word.*`? Also do you happen to know any good documentation for a tokenising approach? – mjwrazor Mar 20 '17 at 15:09
  • 1
    @mjwrazor Easiest way to confirm index usage would be to [explain your query](https://docs.mongodb.com/manual/reference/explain-results/). I'd also suggest posting a new question with details on your use case rather than discussing in the comments here ;-). – Stennie Mar 21 '17 at 23:10
  • What is the query? If I want to have case insensitive search for the same example. – Rama Krishna Apr 04 '17 at 07:26
  • @Parvin Thank you for the query, it works really great :) – deeptimancode Dec 09 '20 at 07:48
224

As Mongo shell support regex, that's completely possible.

db.users.findOne({"username" : /.*son.*/});

If we want the query to be case-insensitive, we can use "i" option, like shown below:

db.users.findOne({"username" : /.*son.*/i});

See: http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-RegularExpressions

James Gan
  • 6,458
  • 3
  • 25
  • 35
  • 1
    Please include a code snippet demonstrating the usage of regular expressions for searching. Answers should include more information than just a link... – maerics May 16 '12 at 05:13
  • 1
    The selected answer didn't work for me, but this one did (I'm executing mongo queries via docker exec commands) I think this one should be the selected answer because it appears to be more versatile. – Arthur Weborg May 19 '17 at 15:13
  • 6
    like the comments in the selected answer I believe `db.users.findOne({"username" : /.*son.*/});` could also be overkill and the regex could simple be `/son/` – Arthur Weborg May 19 '17 at 15:16
  • 2
    More concise way than using $regex – Lionet Chen Mar 06 '18 at 12:22
  • 6
    Edit this to just use `{ username: /son/ }` – Wyck Mar 23 '18 at 19:18
  • 1
    what if 'son' is variable, like the most use cases I guess – Amr Alaa Jul 05 '19 at 16:34
200

https://docs.mongodb.com/manual/reference/sql-comparison/

http://php.net/manual/en/mongo.sqltomongo.php

MySQL

SELECT * FROM users WHERE username LIKE "%Son%"

MongoDB

db.users.find({username:/Son/})
Zheng Kai
  • 3,171
  • 3
  • 16
  • 23
  • 54
    Remove all of query or change it ? most poeple known SQL, it is helpful for understanding MongoDB – Zheng Kai May 16 '12 at 05:20
  • 133
    @maerics personally I found Zheng's inclusion of the MySQL very useful as it provided a point of refence. – Mike Bartlett Jul 11 '13 at 12:11
  • 73
    I also found the SQL reference relevant, I think it should stay. – vikingsteve Nov 11 '13 at 12:35
  • 3
    Indeed. The SQL example is just two lines of text. Some people may prefer to ignore it, while others may benefit from it, and the cost to those in the first group is probably far outweighed by the benefit to those in the second group. – phoog Apr 08 '19 at 16:30
  • Please note that SQL's LIKE does not do regular expressions. The equivalent in MySQL would be REGEXP or RLIKE. – Slaven Rezic Sep 02 '19 at 13:53
  • 1
    @zhengKai What if you want to query username like son, and other value. Is it possible to have multiple conditions? ex: db.users.find({username:/Son/,/Dad/,/Mom/}) to retrieve all usernames that has "Son, dad, mom" etc.. – Jodi Nov 21 '19 at 04:32
88

As of version 2.4, you can create a text index on the field(s) to search and use the $text operator for querying.

First, create the index:

db.users.createIndex( { "username": "text" } )

Then, to search:

db.users.find( { $text: { $search: "son" } } )

Benchmarks (~150K documents):

  • Regex (other answers) => 5.6-6.9 seconds
  • Text Search => .164-.201 seconds

Notes:

  • A collection can have only one text index. You can use a wildcard text index if you want to search any string field, like this: db.collection.createIndex( { "$**": "text" } ).
  • A text index can be large. It contains one index entry for each unique post-stemmed word in each indexed field for each document inserted.
  • A text index will take longer to build than a normal index.
  • A text index does not store phrases or information about the proximity of words in the documents. As a result, phrase queries will run much more effectively when the entire collection fits in RAM.
okoboko
  • 4,062
  • 7
  • 37
  • 65
  • 23
    no, infact text operator does not allow to execute "contains", so it will only return exact word match, the only option currently as of 3.0 is to use regex , i.e. db.users.find( { username:/son/i } ) this one looksup every user containing "son" (case-insenstive) – comeGetSome Sep 18 '15 at 14:56
  • 3
    Do you have to reindex when you add or remove documents to/from the collection? – Jake Wilson Oct 24 '15 at 03:39
  • The title of the question says "contains". full text search is not applicable to the question. – Daniel Viglione Jul 24 '19 at 23:00
  • @comeGetSome You're right. any updates on how to do this in a fast and effective manner? – Alex Totolici May 13 '22 at 12:21
47

As this is one of the first hits in the search engines, and none of the above seems to work for MongoDB 3.x, here is one regex search that does work:

db.users.find( { 'name' : { '$regex' : yourvalue, '$options' : 'i' } } )

No need to create and extra index or alike.

Nitai
  • 1,561
  • 12
  • 10
  • 1
    Regexes need to be sanitized. – sean Dec 12 '19 at 07:08
  • 1
    came from google and this is the only one that works for me. From the docs, the option `i` is for "Case insensitivity to match upper and lower cases." – Matt Feb 09 '21 at 20:49
  • 2022, it is correct answer. Because if I use $regaxe instead '$regex' Pylance give me error. – Alisher Apr 18 '22 at 12:03
  • Hey @Nitai just want question I wan to give for 2 values in regex. I mean either the string matches with value1 or value2 . How can I modify this query? – Bug May 26 '22 at 08:32
20

Here's what you have to do if you are connecting MongoDB through Python

db.users.find({"username": {'$regex' : '.*' + 'Son' + '.*'}})

you may also use a variable name instead of 'Son' and therefore the string concatenation.

Patthebug
  • 4,377
  • 10
  • 42
  • 86
19

Simplest way to accomplish this task

If you want the query to be case-sensitive

db.getCollection("users").find({'username':/Son/})

If you want the query to be case-insensitive

db.getCollection("users").find({'username':/Son/i})
Anurag Misra
  • 1,362
  • 16
  • 22
9

This should do the work

db.users.find({ username: { $in: [ /son/i ] } });

The i is just there to prevent restrictions of matching single cases of letters.

You can check the $regex documentation on MongoDB documentation. Here's a link: https://docs.mongodb.com/manual/reference/operator/query/regex/

turivishal
  • 29,897
  • 7
  • 30
  • 53
tate
  • 109
  • 1
  • 1
7

ideal answer its use index i option for case-insensitive

db.users.findOne({"username" : new RegExp(search_value, 'i') });
Hisham
  • 1,105
  • 1
  • 16
  • 22
7

I use this code and it work for search substring

db.users.find({key: { $regex: new RegExp(value, 'i')}})
5

If you need to do the search for more than one attribute you can use the $or. For example

Symbol.find(
  {
    $or: [
      { 'symbol': { '$regex': input, '$options': 'i' } },
      { 'name': { '$regex': input, '$options': 'i' } }
    ]
  }
).then((data) => {
  console.log(data)
}).catch((err) => {
  console.log(err)
})

Here you are basing your search on if the input is contained in the symbol attribute or the name attribute.

bello hargbola
  • 359
  • 5
  • 8
4

If the regex is not working in your Aggregate solution and you have nested object. Try this aggregation pipeline: (If your object structure is simple then, just remove the other conditions from below query):

db.user.aggregate({$match: 
     {$and:[
   {"UserObject.Personal.Status":"ACTV"},
   {"UserObject.Personal.Address.Home.Type":"HME"},
   {"UserObject.Personal.Address.Home.Value": /.*son.*/ }
   ]}}
   ) 

One other way would be to directly query like this:

db.user.findOne({"UserObject.Personal.Address.Home.Value": /.*son.*/ });
KushalSeth
  • 1,901
  • 1
  • 15
  • 25
3

If your regex includes a variable, make sure to escape it.

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

This can be used like this

new RegExp(escapeRegExp(searchString), 'i')

Or in a mongoDb query like this

{ '$regex': escapeRegExp(searchString) }

Posted same comment here

Scott Wager
  • 462
  • 7
  • 7
3

For aggregation framework


Field search

('$options': 'i' for case insensitive search)

db.users.aggregate([
    {
        $match: {
            'email': { '$regex': '@gmail.com', '$options': 'i' }
        }
    }
]);

Full document search

(only works on fields indexed with a text index

db.articles.aggregate([
    {
        $match: { $text: { $search: 'brave new world' } }
    }
])
F.H.
  • 1,237
  • 1
  • 17
  • 30
1

How to ignore HTML tags in a RegExp match:

var text = '<p>The <b>tiger</b> (<i>Panthera tigris</i>) is the largest <a href="/wiki/Felidae" title="Felidae">cat</a> <a href="/wiki/Species" title="Species">species</a>, most recognizable for its pattern of dark vertical stripes on reddish-orange fur with a lighter underside. The species is classified in the genus <i><a href="/wiki/Panthera" title="Panthera">Panthera</a></i> with the <a href="/wiki/Lion" title="Lion">lion</a>, <a href="/wiki/Leopard" title="Leopard">leopard</a>, <a href="/wiki/Jaguar" title="Jaguar">jaguar</a>, and <a href="/wiki/Snow_leopard" title="Snow leopard">snow leopard</a>. It is an <a href="/wiki/Apex_predator" title="Apex predator">apex predator</a>, primarily preying on <a href="/wiki/Ungulate" title="Ungulate">ungulates</a> such as <a href="/wiki/Deer" title="Deer">deer</a> and <a href="/wiki/Bovid" class="mw-redirect" title="Bovid">bovids</a>.</p>';
var searchString = 'largest cat species';

var rx = '';
searchString.split(' ').forEach(e => {
  rx += '('+e+')((?:\\s*(?:<\/?\\w[^<>]*>)?\\s*)*)';
});

rx = new RegExp(rx, 'igm');

console.log(text.match(rx));

This is probably very easy to turn into a MongoDB aggregation filter.

Tamás Polgár
  • 1,920
  • 4
  • 17
  • 42