765

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?

0

17 Answers 17

1187

You can do it with the following code.

db.users.findOne({"username" : {$regex : "son"}});
Sign up to request clarification or add additional context in comments.

10 Comments

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
@Stennie, then what do you suggest to make efficient use of index and find a substring.
@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 for a simple full-text search, or perhaps build an 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.
Might want to check out full text search in Mongo 2.6
@mjwrazor Still applicable to MongoDB 3.4 as per the $regex documentation page you linked and the information on 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.
|
278

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

6 Comments

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.
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/
More concise way than using $regex
Edit this to just use { username: /son/ }
what if 'son' is variable, like the most use cases I guess
|
236

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/})

6 Comments

Remove all of query or change it ? most poeple known SQL, it is helpful for understanding MongoDB
@maerics personally I found Zheng's inclusion of the MySQL very useful as it provided a point of refence.
I also found the SQL reference relevant, I think it should stay.
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.
@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..
|
110

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.

4 Comments

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)
Do you have to reindex when you add or remove documents to/from the collection?
The title of the question says "contains". full text search is not applicable to the question.
@comeGetSome You're right. any updates on how to do this in a fast and effective manner?
64

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.

5 Comments

Regexes need to be sanitized.
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."
2022, it is correct answer. Because if I use $regaxe instead '$regex' Pylance give me error.
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?
@Nitai above thing is not working with url search google.com/test/test_page
23

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.

1 Comment

above query was not working with url in aggregate under $match operator, like, "details.uri": { "$regex": ".*phubprod.princeton.edu/psp/phubprod.*", "$options": "i" }
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})

3 Comments

how to use variable with regex??
@Hisham this is in fact regex (see the / symbols instead of ' or " for strings).
@Hisham you can just use the Regex class that is new RegExp(variable)
13

I use this code and it work for search substring

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

Comments

12

ideal answer its use index i option for case-insensitive

db.users.findOne({"username" : new RegExp(search_value, 'i') });

1 Comment

Regexes need to be sanitized.
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/

Comments

10

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.

Comments

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.*/ });

Comments

7

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' } }
    }
])

2 Comments

how can i use first option with URL search, like i want to exclude http protocol and query parameters, google.com?id=1 in this can http,www and query params would be ignored
You can pass in any valid regex pattern, see mongodb.com/docs/manual/reference/operator/query/regex
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

Comments

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="https://stackoverflow.com/wiki/Felidae" title="Felidae">cat</a> <a href="https://stackoverflow.com/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="https://stackoverflow.com/wiki/Panthera" title="Panthera">Panthera</a></i> with the <a href="https://stackoverflow.com/wiki/Lion" title="Lion">lion</a>, <a href="https://stackoverflow.com/wiki/Leopard" title="Leopard">leopard</a>, <a href="https://stackoverflow.com/wiki/Jaguar" title="Jaguar">jaguar</a>, and <a href="https://stackoverflow.com/wiki/Snow_leopard" title="Snow leopard">snow leopard</a>. It is an <a href="https://stackoverflow.com/wiki/Apex_predator" title="Apex predator">apex predator</a>, primarily preying on <a href="https://stackoverflow.com/wiki/Ungulate" title="Ungulate">ungulates</a> such as <a href="https://stackoverflow.com/wiki/Deer" title="Deer">deer</a> and <a href="https://stackoverflow.com/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.

Comments

1

Also if you want to find if a sub field contains a string, you can do as follows:

db.getCollection("yourCollectionName").find({ "yourField.yourSubfield": { $regex: 'yourSpecificWord', $options: 'i' } })

Comments

-1

MongoDB 4.2 onwards a new aggregate operator introduced called $regexMatch. While the $regex operator was usable only inside $match stage, the $regexMatch operator can be used with other aggregate stages.

Example: Here we use $project stage that adds an additional field called "usernameMatches". It's value is true if the $regexMatch satisfies the condition or else false.

db.users.aggregate([ 
    {
        $project: { 
            usernameMatches: { 
                $regexMatch: { 
                    input: "$username", 
                    regex: /son/, 
                    options: "i" 
                } 
            },
            username: 1
        } 
    }
]);

Output:-

[
    {        
        _id: "6307353fa2312096b171fe78"
        usernname: "johnson"
        usernameMatches: true
    },
    {        
        _id: "6307353fa2312096b171fe79"
        usernname: "alex"
        usernameMatches: false
    }
    .
    .
    .
]

Similarly 2 additional operators $regexFind and $regexFindAll introduced from 4.2 onward.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.