MongoDb / C# отфильтруйте и получите все вложенные документы

У меня трудности с запросом коллекции Mongo-DB.

Документ, который я использую

public class Customer
{
    public ObjectId Id { get; set; }

    public int CustomerId { get; set; }

    public List<Address> Addresses { get; set; }
}

public class Address
{
    public string Name { get; set; }
}

Некоторые сэмплы-данные

{
    CustomerId: 2,
    Addresses: [
        {
            Name: "Daniel"
        },
        {
            Name: "Eric"
        },
        {
            Name: "Dan"
        }
        ]
}

Теперь я хочу запросить документы по CustomerId и отфильтровать некоторые значения-адреса, чтобы вернуть все адреса с таким именем, как%Dan%. Поскольку сбор адресов может быть огромным, я хочу уже фильтровать эти адреса во время запроса

var mdb = mongoClient.GetDatabase("test");
var collection = mdb.GetCollection<WebApi>("Customer");
var builder = Builders<Customer>.Filter;
var searchTerm = "Dan";

При таком запросе он работает, но содержит все адреса:

var filter = builder.And(builder.Eq("InstallationId", 2),
                         builder.Regex("Addresses.Name", new BsonRegularExpression(".*" + searchTerm + ".*", "i")))
var result = collection.Find(filter).FirstOrDefault();

Что я хотел бы получить:

[
    {
        Name: "Daniel"
    },
    {
        Name: "Dan"
    }
]

или же

{
    CustomerId: 2,
    Addresses: [
        {
            Name: "Daniel"
        },
        {
            Name: "Dan"
        }
        ]
}

Я также попробовал несколько подходов с Aggregate/Match/Project, но не смог заставить его работать. "AsQueryable" также не работает, так как IndexOf не реализован в драйвере.

collection.AsQueryable().Where(x => x.CustomId == 2).SelectMany(x =>
                x.Addresses.Where(a => a.Name.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList();

Используемые версии:

  • MongoDB.Driver v 2.6.1 (также Bson)
  • .Net Framework 4.5.2

1 ответ

Решение

Это должно заставить вас идти:

var result = collection
    .Aggregate()
    .Match(c => c.CustomerId == 2)
    .Project(c => new
        {
            c.CustomerId,
            Addresses = c.Addresses.Where(a => a.Name.IndexOf(searchTerm) != -1)
        })
    .ToList();

Драйвер переведет это на:

db.Customer.aggregate([{
    $match: { CustomerId: 2 }
}, {
    $project: {
        CustomerId: "$CustomerId",
        Addresses: {
            $filter: {
                input: "$Addresses",
                as: "a",
                cond: {
                    $ne: [ { $indexOfBytes: [ "$$a.Name", "Dan" ] }, -1 ]
                }
            }
        },
        _id: 0
    }
}])

Для версии без учета регистра будет возможность использовать $regex на каком-то этапе в будущем (см. https://jira.mongodb.org/browse/SERVER-11947). Однако то, что вы можете сделать только сейчас, это использовать ToUpper():

var result = collection
    .Aggregate()
    .Match(c => c.CustomerId == 2)
    .Project(c => new
        {
            c.CustomerId,
            Addresses = c.Addresses.Where(a => a.Name.ToUpper().IndexOf(searchTerm.ToUpper()) != -1)
        })
    .ToList();
Другие вопросы по тегам