Java Instant type is pushed to Elastic Search with wrong pattern

Hi,

I’am using ES backend. I indexed a date field using a dateValuebinder :

@Component
public class DateValueBinder implements ValueBinder {

    private final DateValueBridge dateValueBridge;

    public DateValueBinder(DateValueBridge dateValueBridge) {
        this.dateValueBridge = dateValueBridge;
    }

    @Override
    public void bind(ValueBindingContext<?> context) {
        context.bridge(
                Date.class,
                dateValueBridge,
                context.typeFactory().asInstant().projectable(Projectable.YES).sortable(Sortable.YES)
        );
    }
}

And the DateValueBridge :

@Component
public class DateValueBridge implements ValueBridge<Date, Instant> {

    @Override
    public Instant toIndexedValue(Date value, ValueBridgeToIndexedValueContext context) {
        return IndexerPreProcessor.processDateSetAtUTCTimezone(value);
    }

    @Override
    public Date fromIndexedValue(Instant value, ValueBridgeFromIndexedValueContext context) {
        return IndexerPreProcessor.processDateGetAtDefaultTimezone(value);
    }

    /**
     * Necessaire pour la recherche multi index
     * @param other
     * @return
     */
    public boolean isCompatibleWith(ValueBridge<?, ?> other) {
        return other instanceof DateValueBridge;
    }
}

When looking at the pushed mapping to ES server concerning the index, it is the following :

"datePrevisionnelle": {
                    "type": "date",
                    "format": "uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZZZZZ"
                }

All is working well with my Query DSL through hsearch API. But doing a native ES query seems to cause a problem with that pattern, for example I generated “by hand” this custom aggregation :

"aggregations": {
		"abstractRangeAggregation": {
			"date_range": {
				"field": "datePrevisionnelle",
				"ranges": [
					{
						"from": "2022-08-01T02:00:00.000000000+02:00",
						"to": "2022-09-01T01:59:59.999999999+02:00"
					},
					{
						"from": "2022-09-01T02:00:00.000000000+02:00",
						"to": "2022-10-01T01:59:59.999999999+02:00"
					},
					{
						"from": "2022-10-01T02:00:00.000000000+02:00",
						"to": "2022-11-01T01:59:59.999999999+02:00"
					},
					{
						"from": "2022-11-01T01:00:00.000000000+01:00",
						"to": "2022-12-01T00:59:59.999999999+01:00"
					},
					{
						"from": "2022-12-01T01:00:00.000000000+01:00",
						"to": "2023-01-01T00:59:59.999999999+01:00"
					},
					{
						"from": "2023-01-01T01:00:00.000000000+01:00",
						"to": "2023-02-01T00:59:59.999999999+01:00"
					}
				]
			}
		},

So I compute the “from” and the “to” keys. And I do the search like hsearch :

DateTimeFormatter esDateTimeFormatter = ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZZZZZ");

return new JSONObject()
											.putOpt("from", r.getLeft() != null ? IndexerPreProcessor.processDateSetAtUTCTimezone(r.getLeft()) : null)
											.putOpt("to", r.getRight() != null ? esDateTimeFormatter.format(IndexerPreProcessor.processDateSetAtUTCTimezone(r.getRight()).atOffset(currentOffsetForMyZone)) : null);

As a result, the first "putOpt(“from”,…) is causing a failure request. On the contrary the second "putOpt(“to”,…) is working fine. Why the first one (“from”) is not ok ? It is the same as the hsearch one isn’t it ? I have to do the trick like the “to” key.

Here the request error (“from” key) :

Request: POST /dossier-read/_search with parameters {from=0, size=0, track_total_hits=true}
Response: 400 'Bad Request' from 'http://127.0.0.1:9200' with body 
{
  "error": {
    "root_cause": [
      {
        "type": "parse_exception",
        "reason": "failed to parse date field [2022-08-01T00:00:00Z] with format [uuuu-MM-dd\u0027T\u0027HH:mm:ss.SSSSSSSSSZZZZZ]: [failed to parse date field [2022-08-01T00:00:00Z] with format [uuuu-MM-dd\u0027T\u0027HH:mm:ss.SSSSSSSSSZZZZZ]]"
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true,
    "failed_shards": [
      {
        "shard": 0,
        "index": "dossier-000001",
        "node": "ltU501gDTRCa7BdpS-lpTQ",
        "reason": {
          "type": "parse_exception",
          "reason": "failed to parse date field [2022-08-01T00:00:00Z] with format [uuuu-MM-dd\u0027T\u0027HH:mm:ss.SSSSSSSSSZZZZZ]: [failed to parse date field [2022-08-01T00:00:00Z] with format [uuuu-MM-dd\u0027T\u0027HH:mm:ss.SSSSSSSSSZZZZZ]]",
          "caused_by": {
            "type": "illegal_argument_exception",
            "reason": "failed to parse date field [2022-08-01T00:00:00Z] with format [uuuu-MM-dd\u0027T\u0027HH:mm:ss.SSSSSSSSSZZZZZ]",
            "caused_by": {
              "type": "date_time_parse_exception",
              "reason": "Text \u00272022-08-01T00:00:00Z\u0027 could not be parsed at index 19"
            }
          }
        }
      }
    ]
  },
  "status": 400
}

Thanks.

At the risk of stating the obvious, you forgot to call esDateTimeFormatter.format( for the from value, so it won’t be formatted correctly?

The voluntary omission is because I do the same like the DateValueBridge, and it does not work.
The returned Instant type for key “from” seems not to be sufficient (the toString() of Instant is like the example :slight_smile: :

2022-08-09T09:07:26.621Z and it is not the pushed pattern “uuuu-MM-dd’T’HH:mm:ss.SSSSSSSSSZZZZZ” according to the error …

DateValueBridge is not the only piece of code involved in serializing dates to JSON. Hibernate Search does not just call toString() to encode an Instant into JSON, because indeed, that would produce the wrong string (not the format expected by Elasticsearch).

DateValueBridge converts a Date to an Instant. Hibernate Search then properly encodes it to JSON by applying a formatter.

Before you ask: no, it’s not currently possible to customize the date format that Hibernate Search uses for Instants; see [HSEARCH-2354] - Hibernate JIRA.