Skip to content

ES|QL API: use a dictionary for named parameters instead of an array of named values #118168

Open
@swallez

Description

@swallez

While working on adding support of named ES|QL query parameters to Elasticsearch clients, we encountered API modelling difficulties that led us to find a number of inconsistencies. Here below an analysis and a proposal to fix this inconsistency.

Analysis

ES|QL query parameters come in two flavors:

  • Positional parameters:

    query: "from foo | where x == ? and y == ?", params: [1, 2]
    

    The position can also be explicit using 1-based indices, e.g. x == ?1 and y == ?2.

  • Named parameters:

    query: "from foo | where x == ?x and y == ?y", params: [{x: 1}, {y: 2}]
    

Named parameters are an array of named values instead of a dictionary. This allows for some surprising valid requests:

  • Last named parameter wins:

    query: "from foo | where x == ?x and y == ?y", params: [{x: 1}, {y: 2}, {x: 3}]
    

    Value 3 is used for x. The duplication would not be possible with a dictionary of values.

  • Positional parameters in the query and named parameter values:

    query: "from foo | where x == ? and y == ?", params: [{x: 1}, {y: 2}]
    

    That should not be allowed (note that a mixed expression like where x == ? and y == ?y isn't allowed).

Representing named values as an array of key-value pairs also causes issues to represent it in the Elasticsearch API specification. It's type is:

params: Array<Value> | Array<SingleKeyDictionary<String, Value>>>

There is no obvious discriminant that would allow users to state their intent (single value or K/V). Also, in languages like Java, lists are type-erased, meaning that would have to be an untyped List<Object> to account for both variants. And deserializing a params property would require some very custom code like what is done in the server.

Note also that if in the future ES|QL allows complex values (i.e. JSON objects) as parameters, we can no more make the distinction between positional parameters with a complex value and named parameters.

Proposal

Named query parameters should be represented as a dictionary (a JSON object), the array being used only for positional parameters.

// named
from foo | where x == ?x and y == ?y", params: {x: 1, y: 2}]

// positional
from foo | where x == ? and y == ?", params: [1, 2]

That way we avoid the issues outlined above and can unambiguously distinguish positional and named parameters. Positional access should also be forbidden for named parameters.

To avoid a breaking API change with the existing syntax, this would come in addition to the existing array-of-named-values format, which would be considered legacy and removed from the docs to discourage its usage (it would have to be removed though if we ever add complex parameter values).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions