NEST V7.12.1 - 检测到类型为 'Elasticsearch.Net.ApiCallDetails' 的 属性 'response' 的自引用循环

NEST V7.12.1 - Self referencing loop detected for property 'response' with type 'Elasticsearch.Net.ApiCallDetails'

我对 ElasticSearch 还很陌生,目前正在尝试使用它来工作,但是在尝试进行 Regex 查询时,出现以下错误:

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'response' with type 'Elasticsearch.Net.ApiCallDetails'. Path 'apiCall.originalException'. at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue) ......

查询:

var ChildFileItems = ElasticClient.Search<FileData>(s => s
                //.From(0).Size(10)
                .Query(q => q
                    .Regexp(r => r
                        .Field(p => p.FilePath)
                        //.Value($"/{SearchPath}\\([A-Z a-z0-9_.-]+)[.]([A-Z a-z0-9]+)/g")
                        .Value(@$"/([A-Z a-z:0-9_.\-]+)/g")
                    )
                )
            );

(我只是想让任何正则表达式查询正常工作)。 当我执行一个没有找到任何结果的 Regex 查询时,它没有给出任何错误,所以它是(我猜)我的对象中的东西。在网上我发现你可以忽略这个错误,但是这些都是针对版本 6 的,我无法在版本 7 中使用它。这是最好的(阅读:我没有错误)版本,但是我仍然得到了错误。

当我执行“正常”匹配查询时,它确实工作正常并且我得到了我的结果。

var settings = new ConnectionSettings(pool, (builtInSerializer, connectionSettings) =>
                new JsonNetSerializer(builtInSerializer, connectionSettings, () => new JsonSerializerSettings
                {
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                }))
                .DefaultFieldNameInferrer(p => p)
                .PrettyJson();
            ;

错误很奇怪;它是 Newtonsoft.Json.JsonSerializationException,但 7.12.1 客户端不在内部使用 Newtonsoft.Json。

使用 Newtonsoft.Json 的序列化程序可以根据您的第二个代码示例配置为 JsonNetSerializer,但此序列化程序用于反序列化您的文档,不会用于反序列化 Elasticsearch.Net.ApiCallDetails,这是一个低级别的客户端类型。

我试图用 Nest 7.12.1 复制错误,但我做不到。然而,正则表达式存在一些问题;

  1. 封闭的 /g 修饰符无效或不受 the regular expression engine 支持,可以省略。
  2. 由于正则表达式值是 verbatim string\ 在查询中发送时是文字 \\,这会导致错误:
// Request
POST http://localhost:9200/default_index/_search?pretty=true&typed_keys=true 
{
  "query": {
    "regexp": {
      "FilePath": {
        "value": "/([A-Z a-z:0-9_.\\-]+)/g"
      }
    }
  }
}

// Response
Status: 400

{
  "error" : {
    "root_cause" : [
      {
        "type" : "query_shard_exception",
        "reason" : "failed to create query: expected ']' at position 24",
        "index_uuid" : "83Lsg5kRR32c6iSK-1L5rw",
        "index" : "default_index"
      }
    ],
    "type" : "search_phase_execution_exception",
    "reason" : "all shards failed",
    "phase" : "query",
    "grouped" : true,
    "failed_shards" : [
      {
        "shard" : 0,
        "index" : "default_index",
        "node" : "1k1iMRXORXSEKvOH4Iz46Q",
        "reason" : {
          "type" : "query_shard_exception",
          "reason" : "failed to create query: expected ']' at position 24",
          "index_uuid" : "83Lsg5kRR32c6iSK-1L5rw",
          "index" : "default_index",
          "caused_by" : {
            "type" : "illegal_argument_exception",
            "reason" : "expected ']' at position 24"
          }
        }
      }
    ]
  },
  "status" : 400
}
  1. 我认为 . 也需要转义(否则匹配任何字符)。

综上所述,这是一个工作示例

private static void Main()
{
    var defaultIndex = "default_index";
    var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
    var settings = new ConnectionSettings(pool, JsonNetSerializer.Default)
        .DefaultIndex(defaultIndex)
        .DefaultFieldNameInferrer(p => p)
        // The following settings are useful during development but come
        // with overhead and so likely don't want them in production.
        .DisableDirectStreaming()
        .PrettyJson()
        .OnRequestCompleted(callDetails =>
        {
            // Added this so that you can see the requests/responses to and
            // from Elasticsearch.

            if (callDetails.RequestBodyInBytes != null)
            {
                var serializer = new JsonSerializer();
                var jObjects = new List<JObject>();
                using (var sr = new StringReader(Encoding.UTF8.GetString(callDetails.RequestBodyInBytes)))
                using (var jsonTextReader = new JsonTextReader(sr))
                {
                    jsonTextReader.SupportMultipleContent = true;
                    while (jsonTextReader.Read())
                        jObjects.Add((JObject)JObject.ReadFrom(jsonTextReader));
                }
                
                var formatting =  jObjects.Count == 1 
                    ? Newtonsoft.Json.Formatting.Indented 
                    : Newtonsoft.Json.Formatting.None;
                var json = string.Join("\n", jObjects.Select(j => j.ToString(formatting)));
                
                Console.WriteLine($"{callDetails.HttpMethod} {callDetails.Uri} \n{json}");
            }
            else
            {
                Console.WriteLine($"{callDetails.HttpMethod} {callDetails.Uri}");
            }
    
            Console.WriteLine();
    
            if (callDetails.ResponseBodyInBytes != null)
            {
                Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" +
                         $"{Encoding.UTF8.GetString(callDetails.ResponseBodyInBytes)}\n" +
                         $"{new string('-', 30)}\n");
            }
            else
            {
                Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" +
                         $"{new string('-', 30)}\n");
            }
        });
        
    var client = new ElasticClient(settings);

    if (client.Indices.Exists(defaultIndex).Exists)
        client.Indices.Delete(defaultIndex);
    
    client.Bulk(b => b
        .Index(defaultIndex)
        .IndexMany(new [] {
            new FileData { FilePath = "Foo.jpg" },
            new FileData { FilePath = "^$%*#@" },
        })
        .Refresh(Refresh.WaitFor)
    );
    
    var searchResponse = client.Search<FileData>(s => s
        .Query(q => q
            .Regexp(r => r
                .Field(p => p.FilePath)
                .Value(@"([A-Z a-z:0-9_\.\-]+)")
            )
        )
    );
}

public class FileData 
{
    public string FilePath {get;set;}
}

找到文件路径为 Foo.jpg

的文档