簡體   English   中英

C#NEST彈性搜索查詢多個條件

[英]C# NEST Elastic Search Query Multiple Conditions

我正在做一些測試來改變我的架構。 我們要刪除MongoDB並改用ElasticSearch。 但是我真的不了解這項技術。 我正在使用NEST作為驅動程序,並且無法翻譯過去用於mongo的查詢。

public async Task<IEnumerable<Keyword>> GetKeywordsAsync(string prefix, int startIndex, int totalItems, int minimumTotalSearch, CancellationToken cancellationToken)
    {
        return await _mongoReader.GetEntitiesAsync<KeywordEntity, Keyword>(CollectionName,
                    queryable =>
                        queryable.Where(entity => entity.KeywordName.StartsWith(prefix) && entity.TotalSearch >= minimumTotalSearch)
                                 .OrderBy(entity => entity.KeywordName)
                                 .Select(_keywordConverter.GetConverter())
                                 .Skip(startIndex)
                                 .Take(totalItems),
                    cancellationToken).ConfigureAwait(false);
    }

public async Task<IEnumerable<TModel>> GetEntitiesAsync<TDocument, TModel>(string collectionName,
            Func<IMongoQueryable<TDocument>, IMongoQueryable<TModel>> getQueryable,
            CancellationToken cancellationToken)
        {
            var documents = GetDocuments<TDocument>(collectionName);
            var query = getQueryable(documents.AsQueryable());
            return await query.ToListAsync(cancellationToken).ConfigureAwait(false);
        }

這是我為ElasticSearch做出的一個簡單發現:

public async Task<IEnumerable<TModel>> FindAsync<TModel, TValue>(string index,
        Expression<Func<TModel, TValue>> findExpression, TValue value, int limit,
        CancellationToken cancellationToken) where TModel : class
    {
        var searchRequest = new SearchRequest<TModel>(index)
        {
            Query =
                Query<TModel>.Match(
                    a => a.Field(findExpression).Query(string.Format(CultureInfo.InvariantCulture, "{0}", value))),
            Size = limit
        };

        var resGet = await _elasticClientFactory.Create().SearchAsync<TModel>(searchRequest, cancellationToken).ConfigureAwait(false);

        return resGet?.Documents;
    }

問題是我無法在Elastic中翻譯查詢Mongo ...

這很痛苦,但這是彈性查詢:

{
  "query": {
    "bool": {
      "must": [
        {"range" : { "totalSearch" : { "gte" : minimumTotalSearch }}},
        {"prefix": { "keywordName": prefix}}
      ]
    }
  },
  "from": startIndex,
  "size": totalItems
}

->解決方案:經過一些艱苦的編碼,我找到了一種在C#中進行查詢的方法:

var result =
            ecf.Create()
                .Search<KeywordEntity>(
                    a => a.Query(
                        z =>
                            z.Bool(
                                e =>
                                    e.Must(r => r.Range(t => t.Field(y => y.TotalSearch).GreaterThanOrEquals(minimumTotalSearch)),
                                        t => t.Prefix(y => y.KeywordName, prefix)))).Index("keywords"));

但是現在我要問自己這是否是執行此查詢的最佳方法(無需跳過/接聽就很容易了)。 作為我的新手,也許有一個更優化的查詢...

您的解決方案看起來不錯,但是有兩點值得強調。

  1. 客戶端是線程安全的,並廣泛使用緩存,因此建議創建一個實例並重用它; 不這樣做將意味着需要根據每個請求重建緩存,從而降低性能。
  2. 由於range查詢查找匹配的文檔或不匹配的文檔,即它是不需要對匹配文檔進行評分的謂詞,因此range查詢可以包裝在bool查詢filter子句中; 這些子句可以由Elasticsearch使用咆哮的位圖進行緩存。

NEST還將QueryContainer (根查詢類型)上的運算符超載,以QueryContainer運算符,以合並它們以構建bool查詢。 然后,您的解決方案將成為(具有上述建議)

var searchResponse = client.Search<KeywordEntity>(s => s
    .Index("keywords")
    .Query(q => q
        .Prefix(p => p.KeywordName, prefix) && +q
        .Range(r => r
            .Field(y => y.TotalSearch)
            .GreaterThanOrEquals(minimumTotalSearch)
        )
    )
);

可以使用分頁.From().Size()別名與.Skip().Take()分別地),以及僅指定部分集的字段是從使用源返回的源過濾 一個更完整的例子是

var client = new ElasticClient();

var minimumTotalSearch = 10;
var prefix = "prefix";
var startIndex = 10;
var totalItems = 10;

var searchResponse = client.Search<KeywordEntity>(s => s
    .Index("keywords")
    .Query(q => q
        .Prefix(p => p.KeywordName, prefix) && +q
        .Range(r => r
            .Field(y => y.TotalSearch)
            .GreaterThanOrEquals(minimumTotalSearch)
        )
    )
    // source filtering
    .Source(sf => sf
        .Includes(f => f
            .Fields(
                ff => ff.KeywordName,
                ff => ff.TotalSearch
            )
        )
    )
    // sorting. By default, documents will be sorted by _score descending
    .Sort(so => so
        .Ascending(a => a.KeywordName)
    )
    // skip x documents
    .Skip(startIndex)
    // take next y documents
    .Take(totalItems)
);

這將建立查詢

{
  "from": 10,
  "size": 10,
  "sort": [
    {
      "keywordName": {
        "order": "asc"
      }
    }
  ],
  "_source": {
    "includes": [
      "keywordName",
      "totalSearch"
    ]
  },
  "query": {
    "bool": {
      "must": [
        {
          "prefix": {
            "keywordName": {
              "value": "prefix"
            }
          }
        }
      ],
      "filter": [
        {
          "range": {
            "totalSearch": {
              "gte": 10.0
            }
          }
        }
      ]
    }
  }
}

最后一件事:)因為在您的Mongo查詢中,您是按前綴升序進行排序的,所以您還可以通過在bool查詢中同時使其成為filter子句來放棄對Elasticsearch查詢中的prefix查詢評分。

查詢將是這樣的。

client.Search<KeywordEntity>(s => s.Index("<INDEX NAME>")
                                    .Type("<TYPE NAME>")
                                    .Query(q =>q
                                        .Bool(b => b.
                                            Must(prefix => prefix.Prefix(pre => pre.OnField("KeywordName").Value("PREFIX QUERY")))
                                            .Must(range => range.Range(ran => ran.OnField("TotalSearch").GreaterOrEquals(minimumTotalSearch)))
                          )).SortAscending("KeywordName")
                          .From(StartIndex)
                          .Size(totalItems));

如果您有任何困難,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM