Как вы отлаживаете свои запросы в Nest?

Я новичок в Nest, и я, скорее всего, не создаю свой запрос, как мне кажется. Мой вопрос больше связан с тем, как научить человека ловить рыбу, а не давать мне рыбу. Однако в качестве примера я воспользуюсь своей текущей проблемой.

У меня есть несколько документов в ElasticSearch типа Series. Я отключу его ниже без атрибутов и общедоступных модификаторов только с информацией, относящейся к запросу:

class Series
{
    string Id {get; set;}
    DateTime StartDate {get; set;}
    DateTime EndDate {get; set;}
    HashSet<Role> ReleasableTo {get; set;}
}

Все они прекрасные и денди. Я могу Get() a Series объект без проблем. Проблема, с которой я сталкиваюсь, пытается выяснить, как Nest форматирует мой запрос. Моя ближайшая цель - найти самый последний Series, который можно разделить на Role.Visitor. Я установил запрос Nest следующим образом:

ISearchResponse<Series> response = client
    .Search<Series>(r => 
         r.Filter(f => 
             f.Term<Role>(t=>t.ReleasableTo.First(), Role.Visitor))
    .SortDescending(ser => ser.EndDate).Size(1));

На мой взгляд, это должно производить запрос, который фильтрует серию, поэтому учитываются только те, которые ReleasableTo my Role.Visitor, обратные сортировки по дате окончания и ограничивают результаты одним возвращенным. Это было бы именно то, что я хочу. В нескольких тысячах записей, которые у меня есть для Series, около 90% соответствуют этому профилю. К сожалению, запрос возвращает 0 результатов. Нет ошибки, просто никаких результатов. Я не знаю, если я неправильно использую API, если Nest создает структуру запроса, которая не имеет смысла, или я просто недостаточно знаю ElasticSearch. Когда я удаляю предложение Filter, я получаю результат, но я не гарантирован, что каждому разрешено его видеть.

Как просмотреть JSON, который производит и отправляет Nest ElasticSearch?

Ответ 1

Вы можете получить значения URL-адреса поискового запроса и тела запроса JSON следующим образом:

var requestURL = response.RequestInformation.RequestUrl;
var jsonBody = Encoding.UTF8.GetString(response.RequestInformation.Request);

Вы можете найти другие полезные свойства в RequestInformation для отладки.

Ответ 2

Мне нравится делать это еще дальше, чем bsarkar предлагает и устраняет необходимость в кругообороте:

var client = new ElasticClient();

var seriesSearch = new SearchDescriptor<Series>();
seriesSearch.Filter(f => f
    .Term<Role>(t => t.ReleasableTo.First(), Role.Visitor))
    .SortDescending(ser => ser.EndDate)
    .Size(1));

string searchJson = Encoding.UTF8.GetString(client.Serializer.Serialize(seriesSearch));

Обратите внимание, что ваш ElasticClient не нуждается в каких-либо свойствах соединения, поэтому у вас нет зависимости от ES node.

Ответ 3

NEST - это барокко .NET API. Для уровня 2.1+ по уровню вызова:

IElasticClient client = new ElasticClient();
var searchDescriptor = new SearchDescriptor<Series>();
var query = Query<Series>.Term(...);
var pretty = query.ToPrettyString(query);
var json = client.ToRawRequest(searchDescriptor.Query(descriptor => query));

На уровне конфигурации:

    var settings = new ConnectionSettings()
                     .PrettyJson().DisableDirectStreaming()
                     .OnRequestCompleted(details=> Debug.WriteLine(Encoding.UTF8.GetString(details.RequestBodyInBytes)));

По уровню ответа просмотрите CallDetails.RequestBodyInBytes.

Используемые расширения:

    /// <summary>
    /// Converts search to raw JSON request for debugging.
    /// </summary>
    /// <typeparam name="T">The type.</typeparam>
    /// <param name="self">The self.</param>
    /// <param name="searchDescriptor">The search descriptor.</param>
    /// <returns>The string.</returns>
    public static string ToRawRequest<T>(this IElasticClient self, SearchDescriptor<T> searchDescriptor) where T : class
    {
        using (var output = new MemoryStream())
        {
            self.Serializer.Serialize(searchDescriptor, output);
            output.Position = 0;
            var rawQuery = new StreamReader(output).ReadToEnd();
            return rawQuery;
        }
    }

    /// <summary>
    /// Prints query into string.
    /// </summary>
    /// <param name="self">The self.</param>
    /// <returns>The value.</returns>
    public static string ToPrettyString(this QueryContainer self)
    {
        using (var settings = new ConnectionSettings())
        {
            var visitor = new DslPrettyPrintVisitor(settings);
            self.Accept(visitor);
            return visitor.PrettyPrint.Replace(Environment.NewLine, string.Empty);
        }                                                                         
    }

Ответ 4

Действительно легко. Если это мой код, который ищет:

var results = client.Search<SearchItem>(s => s.AllIndices()
    .Query(q =>
            q.Term(p => p.LastName, searchItem.LastName)
            && q.Term(p => p.FirstName, searchItem.FirstName)
            && q.Term(p => p.ApplicationCode, searchItem.ApplicationCode)
            )
    .Size(1000)
    );
var list = results.Documents.ToList();

Затем я устанавливаю точку останова на приведенной выше строке. Затем, в Visual Studio Immediate Window, я ввожу это:

?results.ConnectionStatus

и это дает мне следующее:

{StatusCode: 200, 
    Method: POST, 
    Url: http://localhost:9200/_all/searchitem/_search, 
    Request: {
  "size": 1000,
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "lastName": {
              "value": "carr"
            }
          }
        },
        {
          "term": {
            "firstName": {
              "value": "adrian"
            }
          }
        }
      ]
    }
  }
}

Надеюсь, что это поможет.

Ответ 5

Используя последний эластичный поиск 5+, я смог получить мой (благодаря методу Адриана Карра) со следующим:

var jsonOutput = System.Text.Encoding.UTF8.GetString(
    response.ApiCall.RequestBodyInBytes
)

Который дал мне следующий результат:

{
   "from":0,
   "size":0,
   "query":{
      "bool":{
      ...
      }
   }
}

Ответ 6

Вы можете использовать EnableTrace или ConnectionStatusHandler. Подробнее здесь.

Ответ 7

Протестировано в гнезде 6.2.0

Теперь вы можете просто сделать:

#if DEBUG
  connectionSettings.EnableDebugMode(details =>
  {
    Logger.Debug($"ES Request: {Encoding.UTF8.GetString(details.RequestBodyInBytes ?? new byte[0])}");
    Logger.Verbose($"ES Response: {Encoding.UTF8.GetString(details.ResponseBodyInBytes ?? new byte[0])}");
  });
#endif

Вызов EnableDebugMode будет автоматически вызывать DisableDirectStreaming, PrettyJson, а затем OnRequestCompleted, передавая функцию, которую вы ему дали.

ПРИМЕЧАНИЕ. Может быть ошибка где-то в их ответе. По какой-то причине ответ, похоже, не имеет некоторых закрывающих скобок.