Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB and C#: Case insensitive search

I am using MongoDB and the C# driver for MongoDB.

I recently discovered that all queries in MongoDB are case-sensitive. How can I make a case-insensitive search?

I found one way to do this:

Query.Matches(
    "FirstName", 
    BsonRegularExpression.Create(new Regex(searchKey,RegexOptions.IgnoreCase)));
like image 838
Andrew Orsich Avatar asked Dec 16 '10 08:12

Andrew Orsich


People also ask

What is MongoDB C driver?

The MongoDB C Driver, also known as “libmongoc”, is a library for using MongoDB from C applications, and for writing MongoDB drivers in higher-level languages. It depends on libbson to generate and parse BSON documents, the native data format of MongoDB.

Is MongoDB a C++?

Welcome to the documentation site for the official MongoDB C++ driver. You can add the driver to your application to work with MongoDB using the C++11 or later standard.

Can we use MongoDB with C#?

To work with MongoDB you need to download the MongoDB C# driver that supports MongoDB. Working with MongoDB is the same as working in LINQ and Entity Framework. MongoDB contains the same features and the same work style that LINQ has. This is one of the great features of MongoDB.

Can Entity Framework work with MongoDB?

Short answer - no, it's for sure possible, but not reasonable. MongoDB is document database and not support any physical relations between collections. EF is a good fit for relational databases like SQL, MySQL, etc. MongoDB works faster with embedded documents.


9 Answers

The simplest and safest way to do that is using Linq:

var names = namesCollection.AsQueryable().Where(name =>
    name.FirstName.ToLower().Contains("hamster"));

As explained in the tutorial ToLower, ToLowerInvariant, ToUpper and ToUpperInvariant all perform matches in a case insensitive way. After that you can use all the supported string methods like Contains or StartsWith.

This example will generate:

{
    "FirstName" : /hamster/is
}

The i option makes it case insensitive.

like image 146
i3arnon Avatar answered Oct 05 '22 13:10

i3arnon


I've just implemented this much simpler than any of the other suggestions. However I realise due to the age of this question, this functionality may not have been available at the time.

Use the options of the Bson Regular Expression constructor to pass in case insensitivity. I just had a look at the source code and found that 'i' is all you need. For example.

var regexFilter = Regex.Escape(filter);
var bsonRegex = new BsonRegularExpression(regexFilter, "i");

Query.Matches("MyField", bsonRegex);

You shouldn't have to keep records twice for searching.

like image 34
Matt Canty Avatar answered Oct 05 '22 13:10

Matt Canty


try to use something like this:

Query.Matches("FieldName", BsonRegularExpression.Create(new Regex(searchKey, RegexOptions.IgnoreCase)))
like image 20
Andrei Andrushkevich Avatar answered Oct 05 '22 15:10

Andrei Andrushkevich


You will probably have to store the field twice, once with its real value, and again in all lowercase. You can then query the lowercased version for case-insensitive search (don't forget to also lowercase the query string).

This approach works (or is necessary) for many database systems, and it should perform better than regular expression based techniques (at least for prefix or exact matching).

like image 28
Thilo Avatar answered Oct 05 '22 13:10

Thilo


As i3arnon answered, you can use Queryable to do a case insensitive comparison/search. What i found out was, that i could not use string.Equals() method, because is it not supported. If you need to do a comparison, Contains() will unfortunately not be suitable which kept me struggling for a solution, for quite some time.

For anyone wanting to do a string comparison, simply use == instead of .Equals().

Code:

var names = namesCollection.AsQueryable().Where(name =>
    name.FirstName.ToLower() == name.ToLower());
like image 21
Teilmann Avatar answered Oct 05 '22 15:10

Teilmann


You can also use MongoDB's built in filters. It may make it easier for using some of mongo's methods.

var filter = Builders<Model>.Filter.Where(p => p.PropertyName.ToLower().Contains(s.ToLower()));
var list = collection.Find(filter).Sort(mySort).ToList();
like image 22
A_Arnold Avatar answered Oct 05 '22 14:10

A_Arnold


For MongoDB 3.4+ the recommended way is to use indexes. See https://jira.mongodb.org/browse/DOCS-11105?focusedCommentId=1859745&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-1859745

I am successfully searching with case insensitive by: 1. Creating an index with Collation for a locale (e.g: "en") and with a strength of 1 or 2. See https://docs.mongodb.com/manual/core/index-case-insensitive/ for further details

  1. Using the same Collation when performing searches on the MongoDb collection.

As an example:

Create a collation with strength 1 or 2 for case insensitive

private readonly Collation _caseInsensitiveCollation = new Collation("en", strength: CollationStrength.Primary);

Create an index. In my case I index several fields:

private void CreateIndex()
{
    var indexOptions = new CreateIndexOptions {Collation = _caseInsensitiveCollation};
    var indexDefinition
        = Builders<MyDto>.IndexKeys.Combine(
            Builders<MyDto>.IndexKeys.Ascending(x => x.Foo),
            Builders<MyDto>.IndexKeys.Ascending(x => x.Bar));
    _myCollection.Indexes.CreateOne(indexDefinition, indexOptions);
}

When querying make sure you use the same Collation:

public IEnumerable<MyDto> GetItems()
{
    var anyFilter = GetQueryFilter();
    var anySort = sortBuilder.Descending(x => x.StartsOn);  
    var findOptions = new FindOptions {Collation = _caseInsensitiveCollation};

    var result = _salesFeeRules
        .Find(anyFilter, findOptions)
        .Sort(anySort)
        .ToList();

    return result;
}
like image 40
diegosasw Avatar answered Oct 05 '22 15:10

diegosasw


The easiest way for MongoDB 3.4+ is to use one of ICU Comparison Levels

return await Collection()
.Find(filter, new FindOptions { Collation = new Collation("en", strength: CollationStrength.Primary) })
.ToListAsync();

More info https://docs.mongodb.com/manual/reference/method/cursor.collation/index.html

like image 22
dawid debinski Avatar answered Oct 05 '22 15:10

dawid debinski


In case anyone else wondering, using fluent-mongo add-on, you can use Linq to query like that:

public User FindByEmail(Email email)
{
    return session.GetCollection<User>().AsQueryable()
           .Where(u => u.EmailAddress.ToLower() == email.Address.ToLower()).FirstOrDefault();
}

Which results in correct JS-query. Unfortunately, String.Equals() isn't supported yet.

like image 35
Kostassoid Avatar answered Oct 05 '22 15:10

Kostassoid