private Query buildStringQuery(final Condition condition,
                                          final String expression,
                                          final DocRef docRef,
                                          final ElasticIndexField indexField,
                                          final String fieldName) {
        final List<String> terms = tokenizeExpression(expression).filter(term -> !term.isEmpty()).toList();
        if (terms.isEmpty()) {
            return null;
        }

        final BiFunction<String, String, Query> buildQueryFn;
        if (Kind.Keyword.jsonValue().equals(indexField.getNativeType())) {
            // Elasticsearch field mapping type is `keyword`, so generate a term-level query
            buildQueryFn = this::buildKeywordQuery;
        } else {
            // This is a type other than `keyword`, such as `text` or `wildcard`. Perform a full-text match.
            buildQueryFn = this::buildTextQuery;
        }

        switch (condition) {
            case EQUALS -> {
                return buildQueryFn.apply(fieldName, expression);
            }
            case NOT_EQUALS -> {
                return negate(buildQueryFn.apply(fieldName, expression));
            }
            case MATCHES_REGEX -> {
                return QueryBuilders.regexp(q -> q
                        .field(fieldName)
                        .value(expression)
                );
            }
            case IN -> {
                if (terms.size() > 1) {
                    return BoolQuery.of(q -> q
                            .should(terms.stream()
                                    .map(term -> buildQueryFn.apply(fieldName, term))
                                    .toList())
                    )._toQuery();
                } else {
                    return buildQueryFn.apply(fieldName, expression);
                }
            }
            case IN_DICTIONARY -> {
                return buildDictionaryQuery(condition, fieldName, docRef, indexField);
            }
            default -> throw new UnsupportedOperationException("Unsupported condition '" +
                    condition.getDisplayValue() + "' for " + indexField.getDisplayValue() +
                    " field type");
        }
    }
Convert our query objects to an Elasticsearch Query DSL query.
stroom-search/stroom-search-elastic/src/main/java/stroom/search/elastic/search/SearchExpressionQueryBuilder.java