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

时间:2017-04-08 16:58:48

标签: 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",但是当列表的任何值包含在任何文档数组子中时,示例{ {1}}。它应该是所有可能过滤器的OR连接。

使用C#MongoDB驱动程序到目前为止我在[ "Sauna", "Locker" ] ANY IN "amenities"课程中提出了以下方法,但没有返回所需的结果。

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(); }

1 个答案:

答案 0 :(得分:3)

我发布了一些研究方法后面我最终使用的方法,以便将来帮助任何想要做同样事情的人。我找到了如何使用正则表达式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);
}