使用 NEST 排序时出错(未知字段 ignore_unmapped)

Error while sorting with NEST (unknown field ignore_unmapped)

我在使用 NEST 排序时遇到此错误([field_sort] 未知字段 [ignore_unmapped])。原因就在这个地方,评论了就万事大吉了。 请帮助我理解这个问题。

internal static void SortBy(string sortColumn, string sortType, SearchDescriptor<BaseIndexerObject> searchDescriptor)

        {
            Action<string, string, SearchDescriptor<BaseIndexerObject>> algorithm;
            bool algorithmExists = sortAlgorithms.TryGetValue(sortColumn, out algorithm);
            if (algorithmExists)
            {
                algorithm(sortColumn, sortType, searchDescriptor);
                return;
            }

            searchDescriptor.Sort(sortFieldDescriptor =>
            {
                if (sortType == "asc")
                {
                    sortFieldDescriptor.Ascending(asc => sortColumn);
                }
                else
                {
                    sortFieldDescriptor.Descending(desc => sortColumn);
                }

                ParameterExpression param = Expression.Parameter(typeof(BaseIndexerObject), "x");
                Expression<Func<BaseIndexerObject, object>> lambda =
                    Expression.Lambda<Func<BaseIndexerObject, object>>(
                        Expression.Convert(Expression.PropertyOrField(param, sortColumn), typeof(object)), param);

                sortFieldDescriptor.Field(f => f.Field(lambda).IgnoreUnmappedFields());

                return sortFieldDescriptor;
            });
        }

生成的请求和错误响应

POST http://XXX.XXX.XX.XXX:9200/object_folder666_%2A%2Cobject_folder777_%2A/_search?pretty=true&error_trace=true&typed_keys=true HTTP/1.1
Accept: application/json
Content-Type: application/json
User-Agent: elasticsearch-net/7.9.0+54ddce9e1b7e7f755e185f4f6431948fee95e1b9 (Microsoft Windows 10.0.18363; .NET Framework 4.8.4250.0; Nest)
Host: XXX.XXX.XX.XXX:9200
Content-Length: 828

{"from":0,"highlight":{"encoder":"html","fields":{"*":{}},"post_tags":["</mark>"],"pre_tags":["<mark>"]},"query":{"bool":{"must":[{"bool":{"must":[{"bool":{"filter":[{"query_string":{"default_operator":"and","query":"*"}}]}},{"range":{"74":{"gte":0.0,"lte":2147483647.0}}},{"match_all":{}},{"match_all":{}}],"must_not":[{"term":{"79":{"value":300}}},{"bool":{"must":[{"term":{"79":{"value":257}}}],"must_not":[{"term":{"4239":{"value":0}}}]}}]}},{"bool":{"should":[{"bool":{"must":[{"match_all":{}},{"match_all":{}}]}},{"nested":{"path":"12416","query":{"bool":{"must":[{"match_all":{}},{"match_all":{}}]}}}}]}},{"match_all":{}},{"match_all":{}},{"match_all":{}},{"match_all":{}},{"terms":{"833":[-1]}},{"match_all":{}}]}},"size":5,"sort":[{"sortColumn":{"order":"desc"}},{"TimeReal":{"ignore_unmapped":true}}],"timeout":"360s"}


HTTP/1.1 400 Bad Request
content-type: application/json; charset=UTF-8
content-length: 14644

{
  "error" : {
    "root_cause" : [
      {
        "type" : "x_content_parse_exception",
        "reason" : "[1:786] [field_sort] unknown field [ignore_unmapped]",
        "stack_trace" : "[[1:786] [field_sort] unknown field [ignore_unmapped]]; nested: XContentParseException[[1:786] [field_sort] unknown field [ignore_unmapped]];\n\tat org.elasticsearch.ElasticsearchException.guessRootCauses(ElasticsearchException.java:644)\n\tat org.elasticsearch.ElasticsearchException.generateFailureXContent(ElasticsearchException.java:572)\n\tat org.elasticsearch.rest.BytesRestResponse.build(BytesRestResponse.java:149)\n\tat org.elasticsearch.rest.BytesRestResponse.<init>(BytesRestResponse.java:110)\n\tat org.elasticsearch.rest.BytesRestResponse.<init>(BytesRestResponse.java:93)\n\tat org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:238)\n\tat org.elasticsearch.rest.RestController.tryAllHandlers(RestController.java:318)\n\tat org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:176)\n\tat org.elasticsearch.http.AbstractHttpServerTransport.dispatchRequest(AbstractHttpServerTransport.java:318)\n\tat org.elasticsearch.http.AbstractHttpServerTransport.handleIncomingRequest(AbstractHttpServerTransport.java:372)\n\tat org.elasticsearch.http.AbstractHttpServerTransport.incomingRequest(AbstractHttpServerTransport.java:308)\n\tat org.elasticsearch.http.netty4.Netty4HttpRequestHandler.channelRead0(Netty4HttpRequestHandler.java:42)\n\tat org.elasticsearch.http.netty4.Netty4HttpRequestHandler.channelRead0(Netty4HttpRequestHandler.java:28)\n\tat io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat org.elasticsearch.http.netty4.Netty4HttpPipeliningHandler.channelRead(Netty4HttpPipeliningHandler.java:58)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)\n\tat io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)\n\tat io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:615)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:578)\n\tat io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:989)\n\tat io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)\n\tat java.base/java.lang.Thread.run(Thread.java:832)\nCaused by: org.elasticsearch.common.xcontent.XContentParseException: [1:786] [field_sort] unknown field [ignore_unmapped]\n\tat org.elasticsearch.common.xcontent.ObjectParser.lambda$errorOnUnknown(ObjectParser.java:102)\n\tat org.elasticsearch.common.xcontent.ObjectParser.parse(ObjectParser.java:297)\n\tat org.elasticsearch.search.sort.FieldSortBuilder.fromXContent(FieldSortBuilder.java:690)\n\tat org.elasticsearch.search.sort.SortBuilder.parseCompoundSortField(SortBuilder.java:148)\n\tat org.elasticsearch.search.sort.SortBuilder.fromXContent(SortBuilder.java:106)\n\tat org.elasticsearch.search.builder.SearchSourceBuilder.parseXContent(SearchSourceBuilder.java:1170)\n\tat org.elasticsearch.rest.action.search.RestSearchAction.parseSearchRequest(RestSearchAction.java:137)\n\tat org.elasticsearch.rest.action.search.RestSearchAction.lambda$prepareRequest(RestSearchAction.java:113)\n\tat org.elasticsearch.rest.RestRequest.withContentOrSourceParamParserOrNull(RestRequest.java:470)\n\tat org.elasticsearch.rest.action.search.RestSearchAction.prepareRequest(RestSearchAction.java:112)\n\tat org.elasticsearch.rest.BaseRestHandler.handleRequest(BaseRestHandler.java:94)\n\tat org.elasticsearch.xpack.security.rest.SecurityRestFilter.handleRequest(SecurityRestFilter.java:81)\n\tat org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:236)\n\t... 53 more\n"
      }
    ],
    "type" : "x_content_parse_exception",
    "reason" : "[1:786] [field_sort] unknown field [ignore_unmapped]",
    "stack_trace" : "org.elasticsearch.common.xcontent.XContentParseException: [1:786] [field_sort] unknown field [ignore_unmapped]\n\tat org.elasticsearch.common.xcontent.ObjectParser.lambda$errorOnUnknown(ObjectParser.java:102)\n\tat org.elasticsearch.common.xcontent.ObjectParser.parse(ObjectParser.java:297)\n\tat org.elasticsearch.search.sort.FieldSortBuilder.fromXContent(FieldSortBuilder.java:690)\n\tat org.elasticsearch.search.sort.SortBuilder.parseCompoundSortField(SortBuilder.java:148)\n\tat org.elasticsearch.search.sort.SortBuilder.fromXContent(SortBuilder.java:106)\n\tat org.elasticsearch.search.builder.SearchSourceBuilder.parseXContent(SearchSourceBuilder.java:1170)\n\tat org.elasticsearch.rest.action.search.RestSearchAction.parseSearchRequest(RestSearchAction.java:137)\n\tat org.elasticsearch.rest.action.search.RestSearchAction.lambda$prepareRequest(RestSearchAction.java:113)\n\tat org.elasticsearch.rest.RestRequest.withContentOrSourceParamParserOrNull(RestRequest.java:470)\n\tat org.elasticsearch.rest.action.search.RestSearchAction.prepareRequest(RestSearchAction.java:112)\n\tat org.elasticsearch.rest.BaseRestHandler.handleRequest(BaseRestHandler.java:94)\n\tat org.elasticsearch.xpack.security.rest.SecurityRestFilter.handleRequest(SecurityRestFilter.java:81)\n\tat org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:236)\n\tat org.elasticsearch.rest.RestController.tryAllHandlers(RestController.java:318)\n\tat org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:176)\n\tat org.elasticsearch.http.AbstractHttpServerTransport.dispatchRequest(AbstractHttpServerTransport.java:318)\n\tat org.elasticsearch.http.AbstractHttpServerTransport.handleIncomingRequest(AbstractHttpServerTransport.java:372)\n\tat org.elasticsearch.http.AbstractHttpServerTransport.incomingRequest(AbstractHttpServerTransport.java:308)\n\tat org.elasticsearch.http.netty4.Netty4HttpRequestHandler.channelRead0(Netty4HttpRequestHandler.java:42)\n\tat org.elasticsearch.http.netty4.Netty4HttpRequestHandler.channelRead0(Netty4HttpRequestHandler.java:28)\n\tat io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat org.elasticsearch.http.netty4.Netty4HttpPipeliningHandler.channelRead(Netty4HttpPipeliningHandler.java:58)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)\n\tat io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\tat io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\tat io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)\n\tat io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:615)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:578)\n\tat io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:989)\n\tat io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)\n\tat java.base/java.lang.Thread.run(Thread.java:832)\n"
  },
  "status" : 400
}

根据 RFC2616,HTTP/400 响应将不会被缓存,无论存在什么缓存 headers。 此响应未指定明确的 HTTP 缓存生命周期信息,也未指定 Last-Modified 日期。启发式到期通常基于 Last-Modified 日期。缺少 Last-Modified,此响应可能会在每次使用或每次浏览时重新验证 session,具体取决于浏览器配置。

此响应既不包含 ETAG 也不包含 Last-Modified 时间。这将阻止此响应的有条件重新验证。

我不太清楚查询的意图是什么,但是 ignore_unmapped 看起来只有 geo distance sorting 支持。

与其他字段排序,unmapped_type property can be used。我认为您正在寻找类似以下的内容

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

    var defaultIndex = "posts";
    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);
        
    var client = new ElasticClient(settings);

    var sortType = "asc";
    var sortColumn = "TimeReal";

    var response = client.Search<Post>(s => s
        .Sort(sortFieldDescriptor =>
            {
                // TODO: probably cache/source generate the mapping of PropertyInfo -> FieldType
                var property = typeof(BaseIndexerObject).GetProperty(sortColumn);
                FieldType? unmappedType = null;
                
                switch (property.PropertyType.FullName)
                {
                    case "System.Int32":
                        unmappedType = FieldType.Integer;
                        break;
                    // TODO: fill in other relevant Type -> FieldType mappings
                }
                
                sortFieldDescriptor.Field(f => f
                    .Field(sortColumn)
                    .UnmappedType(unmappedType)
                    .Order(sortType == "asc" ? SortOrder.Ascending : SortOrder.Descending)
                );

                return sortFieldDescriptor;
            })
    );
}

public class BaseIndexerObject
{
    public int TimeReal { get; set; }
}

生成查询

POST http://localhost:9200/posts/_search?pretty=true&typed_keys=true 
{
  "sort": [
    {
      "TimeReal": {
        "unmapped_type": "integer",
        "order": "asc"
      }
    }
  ]
}

(顺便说一句,我会看一下用于生成查询的代码。生成的内容中有很多多余的 match_all 查询,这会使查询解析不必要地复杂化服务器).