Elastic NEST Client 如何对嵌套属性进行排序

问题描述

作为 nesT 图书馆的新手,我对那些对我来说毫无意义的文档感到困惑。

一个问题:无法弄清楚如何将查询结果映射到强类型模型,尽管我已经捕获了 json 结果并粘贴到 C# 类中,我尝试过什么,除非我在查询中使用动态类型方法然后结果返回null。

查询返回的json结果派生的模型:

public class Rootobject
{
    public int took { get; set; }
    public bool timed_out { get; set; }
    public _Shards _shards { get; set; }
    public Hits hits { get; set; }
}

public class _Shards
{
    public int total { get; set; }
    public int successful { get; set; }
    public int skipped { get; set; }
    public int Failed { get; set; }
}

public class Hits
{
    public Total total { get; set; }
    public float max_score { get; set; }
    public Hit[] hits { get; set; }
}

public class Total
{
    public int value { get; set; }
    public string relation { get; set; }
}

public class Hit
{
    public string _index { get; set; }
    public string _type { get; set; }
    public string _id { get; set; }
    public float _score { get; set; }
    public _Source _source { get; set; }
}

public class _Source
{
    public DateTime timestamp { get; set; }
    public string level { get; set; }
    public string messageTemplate { get; set; }
    public string message { get; set; }
    public Fields fields { get; set; }
}

public class Fields
{
    public string LogEventCategory { get; set; }
    public string LogEventType { get; set; }
    public string LogEventSource { get; set; }
    public string LogData { get; set; }
    public string MachineName { get; set; }
    public int MemoryUsage { get; set; }
    public int ProcessId { get; set; }
    public string ProcessName { get; set; }
    public int ThreadId { get; set; }
}

查询返回的示例 JSON 结果:

{
  "took" : 16,"timed_out" : false,"_shards" : {
    "total" : 1,"successful" : 1,"skipped" : 0,"Failed" : 0
  },"hits" : {
    "total" : {
      "value" : 8,"relation" : "eq"
    },"max_score" : 1.0,"hits" : [
      {
        "_index" : "webapp-razor-2021.05","_type" : "_doc","_id" : "n2tbTnkBwE4YgJowzRsT","_score" : 1.0,"_source" : {
          "@timestamp" : "2021-05-09T00:41:47.2321845+01:00","level" : "information","messageTemplate" : "{@LogEventCategory}{@LogEventType}{@LogEventSource}{@LogData}","message" : "\"WebApp-RAZOR\"\"Application Startup\"\"System\"\"Application Starting Up\"","fields" : {
            "LogEventCategory" : "WebApp-RAZOR","LogEventType" : "Application Startup","LogEventSource" : "System","LogData" : "Application Starting Up","MachineName" : "DESKTOP-OS52032","MemoryUsage" : 4713408,"ProcessId" : 15152,"ProcessName" : "WebApp-RAZOR","ThreadId" : 1
          }
        }
      },{
        "_index" : "webapp-razor-2021.05","_id" : "oGtdTnkBwE4YgJowuxu_","_source" : {
          "@timestamp" : "2021-05-09T00:43:54.0326968+01:00","MemoryUsage" : 4656048,"ProcessId" : 12504,"_id" : "oWtgTnkBwE4YgJownRtc","_source" : {
          "@timestamp" : "2021-05-09T00:47:02.8954368+01:00","MemoryUsage" : 4717560,"ProcessId" : 17952,"ThreadId" : 1
          }
        }
      }
    ]
  }
}

第二个问题:我正在尝试按嵌套在 JSON 中的属性进行排序,例如名为“LogEventCategory”的属性

nesT 客户端方法

var searchResponse = await _elasticclient.SearchAsync<dynamic>(s => s
   .RequestConfiguration(r => r
        .disableDirectStreaming()
    )
    //.AllIndices()
   .From(0) // From parameter defines the offset from the first result you want to fetch.
   .Size(3) // Size parameter allows you to configure the maximum amount of hits to be returned.
   .Index("webapp-razor-*")
   //.Index("index-1,index-2")
   .Query(q => q
        .MatchAll()
    )
    .sort(so => so
         .Field(fs => fs
             //.Field("@timestamp") // this one seems to work
             .Field("logEventCategory")
             .Order(SortOrder.Ascending)
             //.Order(ColumnSortOrder)
          )
    )
);

知道 nesT 高级客户端旨在映射正确的模型,我意识到使用动态类型是不正确的,但我无法弄清楚为什么使用直接从示例 json 响应创建/映射的类是仍然无法正常工作,并且这样做时返回的结果只是为我提供了前几个属性的空值。

下面的示例,其中只看到前几项(但仍为空)

enter image description here

解决方法

第一个问题:无法弄清楚如何将查询结果映射到强类型模型,尽管我已经捕获了 json 结果并粘贴到 C# 类中,我尝试过什么,除非我在查询中使用动态类型方法然后结果返回null。

从查询返回的json结果派生的模型:

您不需要为整个 JSON 响应定义模型;客户端将负责正确反序列化大部分响应,因此唯一需要创建的 POCO 是针对 _source JSON 对象。根据提供的示例,这将类似于

public class LogMessage
{
    [PropertyName("@timestamp")]
    public DateTimeOffset Timestamp { get; set; }
    
    public string Level {get;set;}
    
    public string MessageTemplate {get;set;}
    
    public string Message {get;set;}
    
    public Dictionary<string,object> Fields {get;set;}
}

我们可以通过创建存根响应并将其传递给客户端进行反序列化来检查此反序列化是否正确

private static void Main()
{
    var defaultIndex = "default_index";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var json = @"{
  ""took"" : 16,""timed_out"" : false,""_shards"" : {
        ""total"" : 1,""successful"" : 1,""skipped"" : 0,""failed"" : 0
  },""hits"" : {
        ""total"" : {
            ""value"" : 8,""relation"" : ""eq""
    
    },""max_score"" : 1.0,""hits"" : [
      {
            ""_index"" : ""webapp-razor-2021.05"",""_type"" : ""_doc"",""_id"" : ""n2tbTnkBwE4YgJowzRsT"",""_score"" : 1.0,""_source"" : {
                ""@timestamp"" : ""2021-05-09T00:41:47.2321845+01:00"",""level"" : ""Information"",""messageTemplate"" : ""{@LogEventCategory}{@LogEventType}{@LogEventSource}{@LogData}"",""message"" : ""\""WebApp-RAZOR\""\""Application Startup\""\""System\""\""Application Starting Up\"""",""fields"" : {
                    ""LogEventCategory"" : ""WebApp-RAZOR"",""LogEventType"" : ""Application Startup"",""LogEventSource"" : ""System"",""LogData"" : ""Application Starting Up"",""MachineName"" : ""DESKTOP-OS52032"",""MemoryUsage"" : 4713408,""ProcessId"" : 15152,""ProcessName"" : ""WebApp-RAZOR"",""ThreadId"" : 1

          }
            }
        },{
            ""_index"" : ""webapp-razor-2021.05"",""_id"" : ""oGtdTnkBwE4YgJowuxu_"",""_source"" : {
                ""@timestamp"" : ""2021-05-09T00:43:54.0326968+01:00"",""MemoryUsage"" : 4656048,""ProcessId"" : 12504,""_id"" : ""oWtgTnkBwE4YgJownRtc"",""_source"" : {
                ""@timestamp"" : ""2021-05-09T00:47:02.8954368+01:00"",""MemoryUsage"" : 4717560,""ProcessId"" : 17952,""ThreadId"" : 1

          }
            }
        }
    ]
  }
}";

    // create a connection that always returns the above response
    var connection = new InMemoryConnection(Encoding.UTF8.GetBytes(json));

    var settings = new ConnectionSettings(pool,connection)
        .DefaultIndex(defaultIndex);
        
    var client = new ElasticClient(settings);

    // actually doesn't matter what query we send through as we'll always get the stubbed response
    var searchResponse = client.Search<LogMessage>(s => s);
    
    foreach (var document in searchResponse.Documents)
    {
        Console.WriteLine($"{document.Timestamp} {document.Message}");
    }
}

产生的结果

9/05/2021 12:41:47 AM +01:00 "WebApp-RAZOR""Application Startup""System""Application Starting Up"
9/05/2021 12:43:54 AM +01:00 "WebApp-RAZOR""Application Startup""System""Application Starting Up"
9/05/2021 12:47:02 AM +01:00 "WebApp-RAZOR""Application Startup""System""Application Starting Up"

第二个问题:我正在尝试按嵌套在 JSON 中的属性进行排序,例如名为“LogEventCategory”的属性

LogMessage POCO 中,fields 被映射为 Dictionary<string,object>,因为我猜键/值可以是动态的。对此对象中的属性进行排序将是

var searchResponse = client.Search<LogMessage>(s => s
    .Sort(so => so
        .Ascending(f => f.Fields["LogEventCategory"])
    )
);

产生以下请求

POST http://localhost:9200/default_index/_search?pretty=true&typed_keys=true 
{
  "sort": [
    {
      "fields.LogEventCategory": {
        "order": "asc"
      }
    }
  ]
}

如果 Elasticsearch 已经推断出 LogEventCategory 的映射,即没有为其提供明确的数据类型映射,那么它将被映射为带有 text {{3} 的 keyword 字段}}(或子字段)。尝试对 multi-field 尚未启用(默认情况下禁用)的 text 字段进行排序将导致错误。通常应该使用 keyword 字段进行排序和聚合,因此如果已经推断出映射,则可以使用 keyword 多字段进行排序

var searchResponse = client.Search<LogMessage>(s => s
    .Sort(so => so
        .Ascending(f => f.Fields["LogEventCategory"].Suffix("keyword"))
    )
);

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...