elastic / elasticsearch-net

This strongly-typed, client library enables working with Elasticsearch. It is the official client maintained and supported by Elastic.
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/index.html
Apache License 2.0
3.57k stars 1.15k forks source link

When using Bool Filter to query, the relationship between Term and Range is not 'And', which is inconsistent with the native DSL query results #8333

Closed d4ilys closed 2 weeks ago

d4ilys commented 2 weeks ago

Elastic.Clients.Elasticsearch version: 8.15.5

Elasticsearch version: 8.9.1

.NET runtime version: 8.0

Operating system version: Windows 11

Description of the problem including expected versus actual behavior: A clear and concise description of what the bug is.

Steps to reproduce: 1.When using Bool Filter to query, the relationship between Term and Range is not 'And', which is inconsistent with the native DSL query results

Expected behavior A clear and concise description of what you expected to happen.

Please refer to the following example

using System.Reflection;
using System.Reflection.Metadata;
using System.Text.Json;
using Elastic.Clients.Elasticsearch;
using Elastic.Transport;

namespace ConsoleApp6
{
    internal class Program
    {
        static readonly ElasticsearchClient Client = new(
            new ElasticsearchClientSettings(new Uri("http://localhost:9200"))
                .Authentication(new BasicAuthentication("elastic", "123456")));

        const string IndexName = "student";

        static async Task Main(string[] args)
        {
           // await Init();
            var searchResponse = await Client.SearchAsync<Student>(search =>
            {
                search.Index(IndexName)
                    .Query(q =>
                    {
                        q.Bool(descriptor =>
                        {
                            descriptor.Filter(m =>
                            {
                                m.Term(mm => mm.Field(f => f.TeacherId).Value("9669669"));
                                m.Range(f => f.DateRange(dateRangeQueryDescriptor =>
                                {
                                    dateRangeQueryDescriptor.Field(c => c.Birthday)
                                        .Gte(DateMath.Anchored("2024-08-01T15:59:56.511"))
                                        .Lte(DateMath.Anchored("2024-12-01T15:59:56.511"));
                                }));
                            });
                        });
                    });
            });
            var source = searchResponse.Hits.Select(hit => hit.Source);
            var serialize = JsonSerializer.Serialize(source,new JsonSerializerOptions
            {
                WriteIndented = true
            });
            Console.WriteLine(serialize);
        }

        static async Task Init()
        {
            await Client.Indices.CreateAsync(IndexName, c =>
            {
                c.Settings(descriptor =>
                {
                    descriptor.NumberOfShards(1);
                    descriptor.NumberOfReplicas(1);
                });
                c.Mappings(descriptor =>
                {
                    descriptor.Properties<Student>(p =>
                    {
                        p.Keyword(k => k.Id);
                        p.Keyword(k => k.Name);
                        p.Keyword(k => k.TeacherId);
                        p.IntegerNumber(k => k.Age);
                        p.Date(k => k.Birthday);
                    });
                });
            });

            var students = new List<Student>()
            {
                new()
                {
                    Id = 1,
                    TeacherId = "9669669",
                    Name = "Tony",
                    Age = 11,
                    Birthday = DateTime.Parse("2024-09-05 16:00:00")
                },
                new()
                {
                    Id = 2,
                    TeacherId = "9669669",
                    Name = "Jesse",
                    Age = 12,
                    Birthday = DateTime.Parse("2024-08-05 16:00:00")
                },
                new()
                {
                    Id = 3,
                    TeacherId = "9669661",
                    Name = "Daily",
                    Age = 13,
                    Birthday = DateTime.Parse("2024-09-01 16:00:00")
                },
                new()
                {
                    Id = 4,
                    TeacherId = "9669662",
                    Name = "Tom",
                    Age = 10,
                    Birthday = DateTime.Parse("2024-09-02 16:00:00")
                },
            };
            var bulkResponse = await Client.BulkAsync(b =>
                b.Index(IndexName).IndexMany(students));
        }
    }

    class Student
    {
        public int Id { get; set; }

        public string TeacherId { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public DateTime Birthday { get; set; }
    }
}

Output result

[
  {
    "Id": 1,
    "TeacherId": "9669669",
    "Name": "Tony",
    "Age": 11,
    "Birthday": "2024-09-05T16:00:00"
  },
  {
    "Id": 2,
    "TeacherId": "9669669",
    "Name": "Jesse",
    "Age": 12,
    "Birthday": "2024-08-05T16:00:00"
  },
  {
    "Id": 3,
    "TeacherId": "9669661",
    "Name": "Daily",
    "Age": 13,
    "Birthday": "2024-09-01T16:00:00"
  },
  {
    "Id": 4,
    "TeacherId": "9669662",
    "Name": "Tom",
    "Age": 10,
    "Birthday": "2024-09-02T16:00:00"
  }
]

Use native DSL query

GET student/_search
{
   "query": {
      "bool": {
        "filter": [
          {
            "term": {
              "teacherId": {
                "value": "9669669"
              }
            }
          },
          {
            "range": {
              "birthday": {
                "gte": "2024-08-01T15:59:56.511",
                "lte": "2024-12-01T15:59:56.511"
              }
            }
          }
        ]
      } 
   }
}

Output result

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.0,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.0,
        "_source" : {
          "id" : 1,
          "teacherId" : "9669669",
          "name" : "Tony",
          "age" : 11,
          "birthday" : "2024-09-05T16:00:00"
        }
      },
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.0,
        "_source" : {
          "id" : 2,
          "teacherId" : "9669669",
          "name" : "Jesse",
          "age" : 12,
          "birthday" : "2024-08-05T16:00:00"
        }
      }
    ]
  }
}
flobernd commented 2 weeks ago

Hi @d4ilys,

this is not an issue with the library. The correct syntax would be:

var searchResponse = await client.SearchAsync<Person>(search => search
    .Index("jointest")
    .Query(q => q
        .Bool(descriptor => descriptor
            .Filter([ // <- note the collection initializer syntax
                m => m.Term(mm => mm.Field(f => f.TeacherId).Value("9669669")),
                m => m.Range(f => f.DateRange(dateRangeQueryDescriptor => dateRangeQueryDescriptor
                    .Field(c => c.Birthday)
                        .Gte(DateMath.Anchored("2024-08-01T15:59:56.511"))
                        .Lte(DateMath.Anchored("2024-12-01T15:59:56.511"))
                ))
            ])
        )
    )
);

Your code did not actually add a Term and a Range query to Filter. Instead, the call to Range has overwritten the Term query that you previously set.

I recommend using expression syntax (x => x.A()) for lambda descriptor actions instead of statement syntax (x => { x.A() }). With statement syntax it's very easy to do mistakes like in your code example.

For lists, there is always an overload that accepts a list/an array of descriptors.

Please let me know, if that solves your issue.

d4ilys commented 2 weeks ago

Hi @flobernd !

Thank you for your reply. The issue has been resolved, but it is indeed easy to make mistakes here😂