c# - 使用 MongoDB 提供程序过滤多个参数

标签 c# mongodb mongodb-query mongodb-.net-driver

我有以下文档架构。

{
      "name":"Name",
      "region":"New Jersey",
      "address":"92 Something Rd",
      "city":"Jersey City",
      "state":"NJ",
      "zipCode":"07302",
      "country":"USA",
      amenities":[
         "Sauna",
         "Locker",
         "Shop"
      ],
      "services":[
         "Car Rental",
         "Transportation"
      ]
}

我想通过一次调用服务器来获取与任何过滤器参数匹配的所有文档,其中 map 1-1 表示 "state"= "NJ"OR "city"= "Jersey City" 而且当列表的任何值包含到任何文档数组子项时,示例 [ "Sauna", "Locker"] ANY IN "amenities"。它应该是所有可能过滤器的 OR 串联。

到目前为止,我使用 C# MongoDB 驱动程序在 MongoRepository 类中提出了以下方法,但没有返回所需的结果。

public async Task<IEnumerable<T>> DocumentsMatchEqFieldValueAsync<T>(string collectionName,
            IDictionary<string, string> fieldsValues = null,
            IDictionary<string, IEnumerable<string>> fieldsWithEnumerableValues = null,
            IEnumerable<ObjectId> ids = null)
{
    var cursor = await GetEqAsyncCursor<T>(collectionName, fieldsValues, fieldsWithEnumerableValues, ids).ConfigureAwait(false);
    return await cursor.ToListAsync().ConfigureAwait(false);
}

protected Task<IAsyncCursor<T>> GetEqAsyncCursor<T>(string collectionName, 
            IDictionary<string, string> fieldsValues = null,
            IDictionary<string, IEnumerable<string>> fieldsWithEnumerableValues = null,
            IEnumerable<ObjectId> ids = null)
{
    var collection = GetCollection<T>(collectionName);
    var builder = Builders<T>.Filter;

    // Not sure if this is the correct way to initialize it because it seems adding an empty filter condition returning ALL document;
    FilterDefinition<T> filter = new BsonDocument(); 

    if (fieldsValues != null &&
        fieldsValues.Any())
    {
        filter = filter | fieldsValues
                    .Select(p => builder.Eq(p.Key, p.Value))
                    .Aggregate((p1, p2) => p1 | p2);
    }

    if (fieldsWithEnumerableValues != null &&
        fieldsWithEnumerableValues.Any())
    {
        filter = filter | fieldsWithEnumerableValues
                    .Select(p => builder.AnyEq(p.Key, p.Value))
                    .Aggregate((p1, p2) => p1 | p2);
    }

    if (ids != null &&
        ids.Any())
    {
        filter = filter | ids
                .Select(p => builder.Eq("_id", p))
                .Aggregate((p1, p2) => p1 | p2);
    }
    return collection.FindAsync(filter);
}

我希望它是通用的,以便客户端可以像这样调用方法。

public async Task should_return_any_records_matching_all_possible_criteria()
{
    // Arrange
    IDocumentRepository documentRepository = new MongoRepository(_mongoConnectionString, _mongoDatabase);

    // Act
    var documents = await documentRepository.DocumentsMatchEqFieldValueAsync<BsonDocument>(Courses,
                fieldsValues: new Dictionary<string, string>
                {
                    { "state", "NJ" },
                    { "city", "Jersey City" }
                },
                fieldsWithEnumerableValues: new Dictionary<string, IEnumerable<string>>
                {
                    { "services", new List<string> { "Car Rental", "Locker" } },
                    { "amenities", new List<string> { "Sauna", "Shop" } }
                });

    // Assert
    documents.ShouldNotBeEmpty();
}

我希望有 "state"= "NJ"OR "city"= "Jersey City"OR "services"CONTAINS ANY OF "Car Rental", "Locker"OR "amenities"CONTAINS ANY OF “桑拿”、“商店”

最佳答案

我在下面发布了我经过一些研究后最终使用的方法,以期为希望这样做的任何人提供 future 帮助。我找到了如何使用正则表达式查询 here ,编写纯 MongoDB 查询并将它们添加到过滤器集合中 here ,以及如何调试生成的查询 here .

在获得所有这些信息并使用 Studio 3T 客户端进行一些实验后,在下面找到该方法。

protected Task<IAsyncCursor<T>> GetEqAsyncCursor<T>(string collectionName,
            IDictionary<string, string> fieldEqValue = null,
            IDictionary<string, string> fieldContainsValue = null,
            IDictionary<string, IEnumerable<string>> fieldEqValues = null,
            IDictionary<string, IEnumerable<string>> fieldElemMatchInValues = null,
            IEnumerable<ObjectId> ids = null)
{
    var collection = GetCollection<T>(collectionName);
    var builder = Builders<T>.Filter;

    IList<FilterDefinition<T>> filters = new List<FilterDefinition<T>>();

    if (fieldEqValue != null &&
        fieldEqValue.Any())
    {
        filters.Add(fieldEqValue
                    .Select(p => builder.Eq(p.Key, p.Value))
                    .Aggregate((p1, p2) => p1 | p2));
    }

    if (fieldContainsValue != null &&
        fieldContainsValue.Any())
    {
        filters.Add(fieldContainsValue
                    .Select(p => builder.Regex(p.Key, new BsonRegularExpression($".*{p.Value}.*", "i")))
                    .Aggregate((p1, p2) => p1 | p2));
    }

    if (fieldEqValues != null &&
        fieldEqValues.Any())
    {
        foreach (var pair in fieldEqValues)
        {
            foreach (var value in pair.Value)
            {
                filters.Add(builder.Eq(pair.Key, value));
            }
        }
    }

    if (fieldElemMatchInValues != null &&
        fieldElemMatchInValues.Any())
    {
        var baseQuery = "{ \"%key%\": { $elemMatch: { $in: [%values%] } } }";
        foreach (var item in fieldElemMatchInValues)
        {
            var replaceKeyQuery = baseQuery.Replace("%key%", item.Key);
            var bsonQuery = replaceKeyQuery.Replace("%values%", 
                        item.Value
                            .Select(p => $"\"{p}\"")
                            .Aggregate((value1, value2) => $"{value1},
 {value2}"));
            var filter = BsonSerializer.Deserialize<BsonDocument>(bsonQuery);
            filters.Add(filter);
        }
    }

    if (ids != null &&
        ids.Any())
    {
        filters.Add(ids
                .Select(p => builder.Eq("_id", p))
                .Aggregate((p1, p2) => p1 | p2));
    }

    var filterConcat = builder.Or(filters);

    // Here's how you can debug the generated query
    //var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer<T>();
    //var renderedFilter = filterConcat.Render(documentSerializer, BsonSerializer.SerializerRegistry).ToString();

    return collection.FindAsync(filterConcat);
}

关于c# - 使用 MongoDB 提供程序过滤多个参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43297901/

相关文章:

reactjs - 从 MongoDB 检索相关元素

c# - 使用通用属性名称处理 lambda 表达式

javascript - MongoDB:填充或查询两个集合

c# - 我如何将其转换为 c#(编码)

c# - 如何解决 MongoDB 溢出排序阶段错误?

python - 如何在 MongoDB 中存储锁对象?

mongodb - 为什么一个 mongoDB 复合索引会影响另一个复合索引?

mongodb - mongo 聚合管道的 $out 阶段未使用节点生效

c# - 音标转语音

c# - 如何在 Dynamics 365 SDK 中检索没有 Guid 的实体?