Skip to content

elasticsearch

ElasticTransformer (BaseTransformer)

Transformer that transforms v0.10.1/v1.0 grammar parse trees into Elasticsearch queries.

Uses elasticsearch_dsl and will produce an elasticsearch_dsl.Q instance.

__default__(self, tree, children, *args, **kwargs) special

Default behavior for rules that only replace one symbol with another

Source code in optimade/filtertransformers/elasticsearch.py
def __default__(self, tree, children, *args, **kwargs):
    """Default behavior for rules that only replace one symbol with another"""
    return children[0]

expression(self, args)

expression: expression_clause ( OR expression_clause )

Source code in optimade/filtertransformers/elasticsearch.py
def expression(self, args):
    # expression: expression_clause ( _OR expression_clause )*
    result = args[0]
    for arg in args[1:]:
        result |= arg
    return result

expression_clause(self, args)

expression_clause: expression_phrase ( AND expression_phrase )*

Source code in optimade/filtertransformers/elasticsearch.py
def expression_clause(self, args):
    # expression_clause: expression_phrase ( _AND expression_phrase )*
    result = args[0]
    for arg in args[1:]:
        result &= arg
    return result

expression_phrase(self, args)

expression_phrase: [ NOT ] ( comparison | "(" expression ")" )

Source code in optimade/filtertransformers/elasticsearch.py
def expression_phrase(self, args):
    # expression_phrase: [ NOT ] ( operator | "(" expression ")" )
    if args[0] == "NOT":
        return ~args[1]
    return args[0]

filter(self, args)

filter: expression*

Source code in optimade/filtertransformers/elasticsearch.py
def filter(self, args):
    # filter: expression*
    if len(args) == 1:
        return args[0]
    return Q("bool", **{"must": args})

fuzzy_string_op_rhs(self, args)

fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value

Source code in optimade/filtertransformers/elasticsearch.py
def fuzzy_string_op_rhs(self, args):
    op = args[0]
    value = args[-1]
    if op == "CONTAINS":
        wildcard = "*%s*" % value
    if op == "STARTS":
        wildcard = "%s*" % value
    if op == "ENDS":
        wildcard = "*%s" % value

    return lambda quantity: Q("wildcard", **{self._field(quantity): wildcard})

length_op_rhs(self, args)

length_op_rhs: LENGTH [ OPERATOR ] value

Source code in optimade/filtertransformers/elasticsearch.py
def length_op_rhs(self, args):
    # length_op_rhs: LENGTH [ OPERATOR ] signed_int
    value = args[-1]
    if len(args) == 3:
        op = args[1]
    else:
        op = "="

    def query(quantity):

        # This is only the case if quantity is an "other" provider's field,
        # in which case, we should treat it as unknown and try to do a null query
        if isinstance(quantity, str):
            return self._query_op(quantity, op, value)

        if quantity.length_quantity is None:
            raise NotImplementedError(
                f"LENGTH is not supported for {quantity.name!r}"
            )
        quantity = quantity.length_quantity
        return self._query_op(quantity, op, value)

    return query

property_zip_addon(self, args)

property_zip_addon: ":" property (":" property)*

Source code in optimade/filtertransformers/elasticsearch.py
def property_zip_addon(self, args):
    raise NotImplementedError("Correlated list queries are not supported.")
    return args

set_op_rhs(self, args)

set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )

Source code in optimade/filtertransformers/elasticsearch.py
def set_op_rhs(self, args):
    # set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ... )
    values = args[-1]
    if not isinstance(values, list):
        if len(args) == 3:
            op = args[1]
        else:
            op = "="
        values = [(op, values)]

    if len(args) == 3:
        op = "HAS " + args[1]
    else:
        op = "HAS"

    return lambda quantity: self._has_query_op(
        [quantity], op, [[value] for value in values]
    )

set_zip_op_rhs(self, args)

set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list | ANY value_zip_list )

Source code in optimade/filtertransformers/elasticsearch.py
def set_zip_op_rhs(self, args):
    # set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list | ANY value_zip_list )
    add_on = args[0]
    values = args[-1]
    if len(args) == 4:
        op = "HAS " + args[2]
    else:
        op = "HAS"
        values = [values]

    return lambda quantity: self._has_query_op([quantity] + add_on, op, values)

value_list(self, args)

value_list: [ OPERATOR ] value ( "," [ OPERATOR ] value )*

Source code in optimade/filtertransformers/elasticsearch.py
def value_list(self, args):
    result = []
    op = "="
    for arg in args:
        if arg in ["<", "<=", ">", ">=", "!=", "="]:
            op = arg
        else:
            result.append(
                (
                    op,
                    arg,
                )
            )
            op = "="
    return result

value_zip(self, args)

value_zip: [ OPERATOR ] value ":" [ OPERATOR ] value (":" [ OPERATOR ] value)*

Source code in optimade/filtertransformers/elasticsearch.py
def value_zip(self, args):
    raise NotImplementedError("Correlated list queries are not supported.")
    return self.value_list(args)

value_zip_list(self, args)

value_zip_list: value_zip ( "," value_zip )*

Source code in optimade/filtertransformers/elasticsearch.py
def value_zip_list(self, args):
    raise NotImplementedError("Correlated list queries are not supported.")
    return args

ElasticsearchQuantity (Quantity)

Elasticsearch-specific extension of the underlying Quantity class.

Attributes:

Name Type Description
name str

The name of the quantity as used in the filter expressions.

backend_field Optional[str]

The name of the field for this quantity in Elasticsearch, will be name by default.

elastic_mapping_type Optional[elasticsearch_dsl.field.Field]

A decendent of an elasticsearch_dsl.Field that denotes which mapping type was used in the Elasticsearch index.

length_quantity Optional[optimade.filtertransformers.elasticsearch.ElasticsearchQuantity]

Elasticsearch does not support length of arrays, but we can map fields with array to other fields with ints about the array length. The LENGTH operator will only be supported for quantities with this attribute.

has_only_quantity Optional[optimade.filtertransformers.elasticsearch.ElasticsearchQuantity]

Elasticsearch does not support exclusive search on arrays, like a list of chemical elements. But, we can order all elements by atomic number and use a keyword field with all elements to perform this search. This only works for elements (i.e. labels in CHEMICAL_SYMBOLS) and quantities with this attribute.

nested_quantity Optional[optimade.filtertransformers.elasticsearch.ElasticsearchQuantity]

To support optimade's 'zipped tuple' feature (e.g. 'elements:elements_ratios HAS "H":>0.33), we use elasticsearch nested objects and nested queries. This quantity will provide the field for the nested object that contains the quantity (and others). The zipped tuples will only work for quantities that share the same nested object quantity.

__init__(self, name, backend_field=None, length_quantity=None, elastic_mapping_type=None, has_only_quantity=None, nested_quantity=None) special

Initialise the quantity from its name, aliases and mapping type.

Parameters:

Name Type Description Default
name str

The name of the quantity as used in the filter expressions.

required
backend_field str

The name of the field for this quantity in Elasticsearch, will be name by default.

None
elastic_mapping_type Field

A decendent of an elasticsearch_dsl.Field that denotes which mapping type was used in the Elasticsearch index.

None
length_quantity ElasticsearchQuantity

Elasticsearch does not support length of arrays, but we can map fields with array to other fields with ints about the array length. The LENGTH operator will only be supported for quantities with this attribute.

None
has_only_quantity ElasticsearchQuantity

Elasticsearch does not support exclusive search on arrays, like a list of chemical elements. But, we can order all elements by atomic number and use a keyword field with all elements to perform this search. This only works for elements (i.e. labels in CHEMICAL_SYMBOLS) and quantities with this attribute.

None
nested_quantity ElasticsearchQuantity

To support optimade's 'zipped tuple' feature (e.g. 'elements:elements_ratios HAS "H":>0.33), we use elasticsearch nested objects and nested queries. This quantity will provide the field for the nested object that contains the quantity (and others). The zipped tuples will only work for quantities that share the same nested object quantity.

None
Source code in optimade/filtertransformers/elasticsearch.py
def __init__(
    self,
    name: str,
    backend_field: str = None,
    length_quantity: "ElasticsearchQuantity" = None,
    elastic_mapping_type: Field = None,
    has_only_quantity: "ElasticsearchQuantity" = None,
    nested_quantity: "ElasticsearchQuantity" = None,
):
    """Initialise the quantity from its name, aliases and mapping type.

    Parameters:
        name: The name of the quantity as used in the filter expressions.
        backend_field: The name of the field for this quantity in Elasticsearch, will be
            ``name`` by default.
        elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which
            mapping type was used in the Elasticsearch index.
        length_quantity: Elasticsearch does not support length of arrays, but we can
            map fields with array to other fields with ints about the array length. The
            LENGTH operator will only be supported for quantities with this attribute.
        has_only_quantity: Elasticsearch does not support exclusive search on arrays, like
            a list of chemical elements. But, we can order all elements by atomic number
            and use a keyword field with all elements to perform this search. This only
            works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities
            with this attribute.
        nested_quantity: To support optimade's 'zipped tuple' feature (e.g.
            'elements:elements_ratios HAS "H":>0.33), we use elasticsearch nested objects
            and nested queries. This quantity will provide the field for the nested
            object that contains the quantity (and others). The zipped tuples will only
            work for quantities that share the same nested object quantity.
    """

    super().__init__(name, backend_field, length_quantity)

    self.elastic_mapping_type = (
        Keyword if elastic_mapping_type is None else elastic_mapping_type
    )
    self.has_only_quantity = has_only_quantity
    self.nested_quantity = nested_quantity