Translator

Translators convert between:

  • Generic query syntax used in internal logic, and

  • Specific query syntax required by a given search engine (e.g., PubMed, EBSCO, Web of Science).

Each translator implements QueryTranslator, the abstract base class from translator_base.py.

Translator responsibilities

A translator must implement the following two class methods:

  • to_generic_syntax(query, *, field_general) -> Query

  • to_specific_syntax(query) -> Query

Each method receives a Query object (the internal AST) and must return a new Query object with appropriately translated search fields and structure.

Versioned translators

Translators are organized in versioned modules such as search_query/pubmed/v1/translator.py. See versioning policy for details. This structure preserves older versions, enabling reproducible translations and ensuring backward compatibility.

A central registry in search_query.translator provides a TRANSLATORS mapping, which dynamically selects the appropriate translator version at runtime. Invoking translate(..., translator_version="latest") loads the most recent version for the specified platform.

To introduce a new translator version, duplicate the previous versioned directory, update the implementation as needed, and register the new version in the TRANSLATORS dictionary.

Utility methods provided

The base class (QueryTranslator) provides the following utilities:

  • move_field_from_operator_to_terms(query): Moves a shared search field from the operator level to each child query.

  • flatten_nested_operators(query): Merges nested logical operators of the same type into a flat structure.

  • move_fields_to_operator(query): If all children have the same field, moves it to the parent node.

Search field mapping

Field mapping is expected to be defined in a constants_<source>.py file and typically includes:

  • syntax_str_to_generic_field_set(): maps specific syntax string (e.g., TI, AB) to set of generic `Fields`. When the set contains multiple elements, the query must be extended with OR (see PubMed translator: _expand_combined_fields()).

  • generic_field_to_syntax_field(): maps generic Fields to platform-specific syntax (set of fields). When combined fields are available, the query must be adapted before (see PubMed tranlsator: _combine_tiab()).

Each translator should:

  1. Translate fields in both directions

  2. Handle combined fields (e.g., [tiab] in PubMed)

  3. Optionally restructure the query for consistency

Code skeleton

from search_query.constants_example import generic_field_set_to_syntax_set
from search_query.constants_example import map_field
from search_query.query import Query
from search_query.translator_base import QueryTranslator


class CustomTranslator(QueryTranslator):
    """Translator for Custom queries."""

    @classmethod
    def to_generic_syntax(cls, query: Query, *, field_general: str) -> Query:
        query = query.copy()
        cls.translate_fields_to_generic(query)
        return query

    @classmethod
    def to_specific_syntax(cls, query: Query) -> Query:
        query = query.copy()
        cls._translate_fields(query)
        return query

    @classmethod
    def translate_fields_to_generic(cls, query: Query) -> None:
        if query.field:
            fields = map_field(query.field.value)
            if len(fields) == 1:
                query.field.value = fields.pop()
            else:
                raise NotImplementedError
        for child in query.children:
            cls.translate_fields_to_generic(child)

    @classmethod
    def _translate_fields(cls, query: Query) -> None:
        if query.field:
            specific = generic_field_to_syntax_field(query.field.value)
            query.field.value = specific

        for child in query.children:
            cls._translate_fields(child)

Advanced features

Some translators include advanced restructuring logic:

  • PubMed: supports [tiab] expansion into OR combinations of [ti] and [ab]