Sorting Search Results
By default, Elasticsearch orders results by relevance score (_score). Custom sorting is supported for fields of type keyword, numeric types, geo_point, and date. Sorting direction is specified using asc or desc.
GET /products/_search
{
"query": { "match_all": {} },
"sort": [
{ "category": "asc" },
{ "price": "desc" }
]
}
Geospatial sorting uses _geo_distance with a reference point:
GET /stores/_search
{
"query": { "match_all": {} },
"sort": [
{
"_geo_distance": {
"location": { "lat": 30.27, "lon": 120.16 },
"order": "asc",
"unit": "miles"
}
}
]
}
Pagination Control
Elasticsearch returns only the first 10 hits by default. To retrieve other result windows, use from (starting offset) and size (number of documents):
GET /products/_search
{
"query": { "match_all": {} },
"from": 50,
"size": 25,
"sort": [{ "updated_at": "desc" }]
}
Note: Deep pagination (e.g.,
from > 10,000) incurs high memory and CPU overhead due to how Elasticsearch merges results across shards. For large offsets, prefersearch_afteror cursor-based navigation.
Result Highlighting
Highlighting wraps matching terms in configurable HTML or plain-text tags. It requires a full-text query (e.g., match, not match_all) and applies only to analyzde fields.
GET /articles/_search
{
"query": {
"match": { "content": "distributed search" }
},
"highlight": {
"fields": {
"content": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"]
},
"title": {}
}
}
}
To access highlighted fragments programmatically in Java, extract them from SearchHit.getHighlightFields():
HighlightField titleHighlight = hit.getHighlightFields().get("title");
if (titleHighlight != null) {
String highlightedTitle = titleHighlight.getFragments()[0].string();
System.out.println(highlightedTitle);
}
Java REST Client Examples
Basic Match-All Query with Result Parsing
public void executeMatchAll() throws IOException {
SearchRequest req = new SearchRequest("inventory");
req.source().query(QueryBuilders.matchAllQuery());
SearchResponse resp = client.search(req, RequestOptions.DEFAULT);
SearchHits hits = resp.getHits();
long totalCount = hits.getTotalHits().value;
System.out.printf("Total matched: %d\n", totalCount);
for (SearchHit hit : hits.getHits()) {
String rawJson = hit.getSourceAsString();
InventoryItem item = JsonMapper.parse(rawJson, InventoryItem.class);
System.out.println(item.getName() + " — " + item.getPrice());
}
}
Multi-Field Full-Text Search
public void executeMultiFieldSearch() throws IOException {
SearchRequest req = new SearchRequest("inventory");
req.source()
.query(QueryBuilders.multiMatchQuery("wireless headset", "name", "description", "tags"))
.size(15);
SearchResponse resp = client.search(req, RequestOptions.DEFAULT);
for (SearchHit hit : resp.getHits().getHits()) {
System.out.println(hit.getSourceAsString());
}
}
Boolean Filtering with Range and Term Constraints
public void executeFilteredSearch() throws IOException {
BoolQueryBuilder filter = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("in_stock", true))
.filter(QueryBuilders.rangeQuery("rating").gte(4.2))
.filter(QueryBuilders.rangeQuery("price").lte(199.99));
SearchRequest req = new SearchRequest("inventory");
req.source().query(filter).sort("rating", SortOrder.DESC);
SearchResponse resp = client.search(req, RequestOptions.DEFAULT);
// process hits...
}
Paginated Sorted Query with Highlighting
public void executePaginatedHighlightedSearch() throws IOException {
SearchRequest req = new SearchRequest("articles");
req.source()
.query(QueryBuilders.matchQuery("body", "cloud infrastructure"))
.highlighter(new HighlightBuilder()
.field("title")
.field("body")
.preTags("<mark>")
.postTags("</mark>"))
.from(0)
.size(10)
.sort("published_date", SortOrder.DESC);
SearchResponse resp = client.search(req, RequestOptions.DEFAULT);
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, HighlightField> highlights = hit.getHighlightFields();
HighlightField titleHl = highlights.get("title");
if (titleHl != null) {
System.out.println("Title: " + titleHl.getFragments()[0]);
}
}
}