-1

Hi i am new to Elasticsearch and trying to implement a solution using spring-data-elasticsearch. My indexed data looks like this:

[
  {
    "worker": "A",
    "availability": [
      {
        "startDate": "2020-01-12",
        "endDate": "2020-02-12"
      },
      {
        "startDate": "2020-04-12",
        "endDate": "2020-05-12"
      }
    ]
  },
  {
    "worker": "B",
    "availability": [
      {
        "startDate": "2020-04-12",
        "endDate": "2020-11-12"
      }
    ]
  }
]

By referring the elastic docs i planned on using range query to fetch the records for a specified rage of dates, for example i wanted to fetch the available worker between "2020-05-12 to 2020-06-12". This is the query that i formed:

{
    "query": {
        "bool": {
            "must": [
                {
                    "nested": {
                        "query": {
                            "range": {
                                "availability.start_date": {
                                    "from": "2020-05-12T00:00:00.000Z",
                                    "to": "2020-06-12T00:00:00.000Z",
                                    "include_lower": true,
                                    "include_upper": true,
                                    "boost": 1.0
                                }
                            }
                        },
                        "path": "availability",
                        "ignore_unmapped": false,
                        "score_mode": "none",
                        "boost": 1.0
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1.0
        }
    }
}

The above query shows empty hits when its executed, but when i use the dates that is indexed then i am able to get the records (ex if i specify the dates as "2020-04-12 to 2020-11-12" the worker B results are displayed). As per the range query it should have worked for my earlier case as-well if i am not wrong. Is there anything wrong in the approach that i followed. Please advice.

Sandeep K Nair
  • 2,512
  • 26
  • 26

1 Answers1

2

You have no worker whose start_date (!!) is between 2020-05-12 and 2020-06-12. I think you need to proceed differently for what you're trying to achieve.

Since you're trying to match on ranges, it would probably be easier to leverage the date_range field type. Your mapping should look like this:

PUT your-index
{
  "mappings": {
    "properties": {
      "availability": {
        "type": "date_range", 
        "format": "yyyy-MM-dd"
      }
    }
  }
}

Then you can index all your worker's availabilities like this:

{
  "worker": "A",
  "availability": [
    {
      "gte": "2020-01-12",
      "lte": "2020-02-12"
    },
    {
      "gte": "2020-04-12",
      "lte": "2020-05-12"
    }
  ]
}
{
  "worker": "B",
  "availability": [
    {
      "gte": "2020-04-12",
      "lte": "2020-11-12"
    }
  ]
}

And then you can perform the search you want like this:

{
  "query": {
    "range": {
      "availability": {
        "gte": "2020-05-12",
        "lte": "2020-06-12",
        "relation": "contains"
      }
    }
  }
}

And you'd find only worker B satisfies the condition.

UPDATE

Your query in Java needs to be like this:

    final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

    queryBuilder.must(QueryBuilders.matchQuery("name", "A"));

    RangeQueryBuilder availability = QueryBuilders.rangeQuery("availability")
           .gte(query.getStartDate())
           .lte(query.getEndDate());

    queryBuilder.must(availability);

    Pageable pageable = PageRequest.of(pageNumber, pageSize);

    // @formatter:off
    return new NativeSearchQueryBuilder()
            .withPageable(pageable)
            .withQuery(queryBuilder)
            .build();
Val
  • 207,596
  • 13
  • 358
  • 360
  • Hi @Val i tried your approach but i am getting an exception like `org.elasticsearch.ElasticsearchException: Elasticsearch exception [type=mapper_parsing_exception, reason=error parsing field [availability.gte], expected an object but got gte]` – Sandeep K Nair Jan 11 '21 at 18:29
  • You must have missed a step, can you double check? I tested the above and it worked out. Can you tell which version of ES you're using? – Val Jan 11 '21 at 19:30
  • Hi @Val here is a minimum reproduction of the project https://github.com/sandeepsuvit/search-poc Would you be kind enough to point me in the right direction, do let me know if i am missing something in the configuration. – Sandeep K Nair Jan 11 '21 at 19:49
  • 1
    Kudos for this!!! [this](https://github.com/sandeepsuvit/search-poc/blob/master/src/main/java/com/search/documents/Worker.java#L25) should be of type `FieldType.Date_Range`, you don't need class `Availability`, it should simply be a Map with a `gte` and `lte` keys – Val Jan 11 '21 at 20:49
  • Hi @Val thanks for checking, but when i try to index as an array of `availability` its throwing an error like: JSON parse error: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token (through reference chain: com.search.documents.Worker[\"availability\"]) – Sandeep K Nair Jan 12 '21 at 05:24
  • Is your repository updated? Why is the Map value a String and not a Date? Your [test search query](https://github.com/sandeepsuvit/search-poc/commit/1c6cf36ce6e4fa305809676d1d535faa0d642776#diff-7ff7aebca7b1a795431898fce777fac00a6b71373935bb17023b13e1051b2202R61-R65) is not correct by the way – Val Jan 12 '21 at 05:46
  • 1
    The error you get is because `availability` should actually be `List>` or `List>` – Val Jan 12 '21 at 05:49
  • Oops sorry that was my mistake. Corrected it now and updated my repo. I am able to index the data now and query the results. This is the query that i am forming now. Is this correct? ```java final RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("availability"); queryBuilder.gte(query.getStartDate()); queryBuilder.lte(query.getEndDate()); return new NativeSearchQueryBuilder() .withQuery(queryBuilder) .build(); ``` – Sandeep K Nair Jan 12 '21 at 06:29
  • 1
    Great! As I commented earlier, your query is not correct. I've updated my answer to show you the right way. – Val Jan 12 '21 at 06:51
  • Wow thanks a lot for that, really appreciate it. Now its working perfect. I do have one more question if I may. If say suppose I need to add one more field called `partial: true` to `availability` indicating that a worker is only available partially on a particular day and I wanted this flag also to be a query parameter, how different would that be? I think we cannot add it to the `date_range` map object `availability` in that case right? – Sandeep K Nair Jan 12 '21 at 07:00
  • Oh i get it. Something like this right? ```java public class Availability { @Field(type = FieldType.Boolean) private boolean partial; @Field(type = FieldType.Date_Range, format = DateFormat.custom, pattern = "uuuu-MM-dd") private List> dates; } ... // Then in the parent Worker.java mapping to Availability @Field(type = FieldType.Nested) private List availability; ``` – Sandeep K Nair Jan 12 '21 at 07:07
  • 1
    Just `private Map dates;` inside `Availability` because at the Worker level you have a List of `Availability` already – Val Jan 12 '21 at 07:09
  • Exactly. That was a typo my bad, I have updated my code. Is that what you are referring to bytwy? – Sandeep K Nair Jan 12 '21 at 07:13
  • Yes, that's correct now. And since you've introduced a nested type, now the query will need to be nested as well, but that's easy to do – Val Jan 12 '21 at 07:14
  • Hi @Val, I updated my query in the code and I guess its correct from a nested perspective. But the results doesn't show up now. Is it because of the query formed? ```java queryBuilder.must(QueryBuilders.termQuery("availability.partial", query.isPartial())); RangeQueryBuilder availability = QueryBuilders.rangeQuery("availability.dates") .gte(query.getStartDate()) .lte(query.getEndDate()); queryBuilder.must(availability); ``` – Sandeep K Nair Jan 12 '21 at 07:27
  • Would you ming creating a new question (with properly formatted code), this one is getting crowded :-) – Val Jan 12 '21 at 07:29
  • Sure would love to do that. Here is the reference to that question https://stackoverflow.com/questions/65679710/elasticsearch-query-on-array-of-composite-objects-along-with-date-ranges – Sandeep K Nair Jan 12 '21 at 07:38
  • Was it for me? I upvoted your answer already – Sandeep K Nair Jan 12 '21 at 15:53
  • Sorry I didn't notice that option on the screen though. Done! :) and thanks for pointing me in the right direction – Sandeep K Nair Jan 12 '21 at 15:57
  • Thank you very much! – Val Jan 12 '21 at 15:58