简体   繁体   中英

Using $in clause for insensitive search in mongo db driver c#

I am wondering how to use the case insensitive for $in expressions.

According to the official MongoDB manual you can do this:

{ name: { $in: [ /^acme/i, /^ack/ ] } }

I tested out this on Compass and it's working fine, search is insensitive.

I need to this using the Mongo Driver in C#.

I am doing this:

  var array = new BsonArray(companyNames);

  var filter = new BsonDocument { { "Name", new BsonDocument { { "$in", new BsonArray(array) }} } };
  var result = _collection.Find(filter).ToList();

companyNames is a string[]

However this retrieves me only exact matches. It's kind of obvious because I am not including the "regex" expression. But I don't know how I am able to include the regex in a string.

The workaround is to create an $or expresion with regex for every company name.

Does anyone knows how to do this?

Thanks

With mongo-csharp-driver , you can utilise MongoDB.Bson.BsonRegularExpression . You can either perform:

var arrayIn = new BsonArray().Add(
                      new BsonRegularExpression("^acme", "i")
                  ).Add(
                      new BsonRegularExpression("^ack"));
var filter = new BsonDocument { { "name", new BsonDocument { { "$in", arrayIn }} } };
var cursor = collection.Find(filter).ToList();

Or alternatively, instead of string use Regex and RegexOptions :

var arrayIn = new BsonArray().Add(
                      new BsonRegularExpression(
                          new Regex(
                              "^acme", RegexOptions.IgnoreCase))
                  ).Add(new BsonRegularExpression(
                              "^ack"));

here's an elegant solution using a text index on the company name field.

using MongoDB.Entities;

namespace StackOverflow
{
    class Program
    {
        public class Company : Entity
        {
            public string Name { get; set; }
        }

        static void Main(string[] args)
        {
            new DB("test");

            DB.Index<Company>()
              .Key(c => c.Name, KeyType.Text)
              .Option(o => o.Background = false)
              .Create();

            var c1 = new Company { Name = "Ackme" };
            var c2 = new Company { Name = "Acme" };
            var c3 = new Company { Name = "Lackme" };
            var c4 = new Company { Name = "Hackme" };

            c1.Save(); c2.Save(); c3.Save(); c4.Save();

            var names = new[] { "ackme", "acme" };

            var result = DB.SearchText<Company>(string.Join(" ", names));
        }
    }
}

mind you, the above uses the convenience library MongoDB.Entities . however the concepts are the same but the syntax for the official driver is cumbersome compared to the above.

My solution was an overloaded .In() as an extension method, taking an extra parameter for case-insensitivity:

public static class MongoExtensions
{
    public static FilterDefinition<TDocument> In<TDocument>(
        this FilterDefinitionBuilder<TDocument> builder,
        Expression<Func<TDocument, object>> expr,
        IEnumerable<string> values,
        bool ignoreCase)
    {
        if (!ignoreCase)
        {
            return builder.In(expr, values);
        }

        var filters = values
            .Select(v => builder.Regex(expr, new BsonRegularExpression($"^{Regex.Escape(v)}$", "i")));

        return builder.Or(filters);
    }
}

Then you can simply:

var filter = Builders<Companies>.Filter.In(c => c.Name, companyNames, true);
var result = _collection.Find(filter).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM