722

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
  • 69,215
  • 39
  • 136
  • 164
johnny
  • 8,696
  • 6
  • 25
  • 36

15 Answers15

1108

You can do it with the following code.

db.users.findOne({"username" : {$regex : "son"}});
Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
Parvin Gasimzade
  • 25,180
  • 8
  • 56
  • 83
  • 27
    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
  • 12
    @Stennie, then what do you suggest to make efficient use of index and find a substring. – Blue Sky Nov 07 '12 at 19:31
  • 5
    @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
  • 3
    Might want to check out full text search in Mongo 2.6 – wprl Sep 05 '14 at 18:38
  • @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
263

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,988
  • 4
  • 28
  • 36
223

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,473
  • 3
  • 17
  • 23
  • 56
    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
  • 140
    @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
  • 77
    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.. – JayC Nov 21 '19 at 04:32
105

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.
firstdorsal
  • 401
  • 5
  • 17
okoboko
  • 4,332
  • 8
  • 40
  • 67
  • 27
    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
  • 1
    @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
59

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,821
  • 1
  • 14
  • 10
  • 2
    Regexes need to be sanitized. – sean Dec 12 '19 at 07:08
  • 2
    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
  • @Nitai above thing is not working with url search google.com/test/test_page – Apurv Chaudhary Jul 12 '22 at 17:52
22

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,647
  • 11
  • 50
  • 91
  • above query was not working with url in aggregate under $match operator, like, "details.uri": { "$regex": ".*phubprod.princeton.edu/psp/phubprod.*", "$options": "i" } – Apurv Chaudhary Jul 12 '22 at 17:54
21

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,516
  • 18
  • 24
12

ideal answer its use index i option for case-insensitive

db.users.findOne({"username" : new RegExp(search_value, 'i') });
Hisham
  • 1,279
  • 1
  • 17
  • 23
12

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
  • 34,368
  • 7
  • 36
  • 59
tate
  • 159
  • 1
  • 2
11

I use this code and it work for search substring

db.users.find({key: { $regex: new RegExp(value, 'i')}})
robocon321
  • 363
  • 5
  • 8
9

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
  • 435
  • 6
  • 9
8

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
  • 3,265
  • 1
  • 26
  • 29
6

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
  • 758
  • 11
  • 9
6

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,456
  • 1
  • 20
  • 34
  • how can i use first option with URL search, like i want to exclude http protocol and query parameters, **http://www.google.com?id=1** in this can http,www and query params would be ignored – Apurv Chaudhary Jul 12 '22 at 18:02
  • You can pass in any valid regex pattern, see https://www.mongodb.com/docs/manual/reference/operator/query/regex/ – F.H. Jul 13 '22 at 12:02
2

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
  • 2,082
  • 5
  • 21
  • 48