2017-04-08 3 views
0

私は以下のドキュメントスキーマを持っています。MongoDBプロバイダで複数のパラメータをフィルタリングする

{ 
     "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" 
     ] 
} 

私は、リストの任意の値は、任意のドキュメント配列の子に含まれているとき、マップ1-1は"state" = "NJ" OR "city" = "Jersey City"を意味するだけでなく、フィルタ引数のいずれかに一致するすべての文書を取得するには、サーバーへの1つの呼び出し、例を挙げて欲しい[ "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 "Sauna", "Shop"のドキュメントが必要です。

答えて

1

私は方法の下で投稿しています。私は同じことをしている誰かの将来の助けのためにいくつかの研究の後に使い終​​わった。私は正規表現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); 
} 
関連する問題