Builder API
Query
The builder generates Elasticsearch Query DSL. Supply DSL through applySelect for computed results and aggregates, not SQL functions. For example, to count matching records:
lambda.query(UserInfo.class)
.applySelect("{\"aggs\":{\"count\":{\"filter\":{\"match_all\":{}}}}}")
.queryForMapList();
See Query Operations for field selection and result reading.
Pagination
Ordinary queries use from and size; a separate count request obtains the total. The builder does not automatically replace deep paging with Scroll or search_after.
Grouped queries use composite aggregation with after_key paging. See Pagination for limits, count meanings, and usage.
Where Builder
Matching conditions generated by eq still follow the field mapping. Store exact business identifiers in keyword fields; do not expect an analyzed text field to behave like ordinary string equality.
Null inside NOT IN does not have SQL's three-valued semantics. To exclude both a specified age and null fields, remove nulls from the input collection and use isNotNull explicitly:
lambda.query(UserInfo.class)
.notIn(UserInfo::getAge, List.of(20))
.isNotNull(UserInfo::getAge)
.queryForList();
Predicate Values
text and keyword have no write-time length constraint equivalent to VARCHAR(100). Long strings remain intact in _source; the builder does not truncate or validate their length.
ignore_above affects indexing, not write acceptance. Validate business length limits before writing.
Group By
groupBy maps to Elasticsearch aggregation. Expressions in applySelect must also use aggregation DSL, not SQL such as SUM(age). For example, count rows by age:
lambda.query(UserInfo.class)
.applySelect("{\"aggs\":{\"cnt\":{\"filter\":{\"match_all\":{}}}}}")
.groupBy("age")
.queryForMapList();
Use aggregation-capable fields, such as numeric or keyword fields.