Data ingestion and query
This topic describes how to ingest data into the PolarDB-X Search engine and how to run full-text search, vector search (KNN), hybrid queries, and aggregations.
Data ingestion
Write a single document
# Write with a specified document ID
curl -XPUT "https://<Search engine endpoint>:9200/products/_doc/1" \
-u "<username>:<password>" -k \
-H "Content-Type: application/json" \
-d '{
"name": "Wireless Bluetooth headphones",
"description": "Premium noise-canceling Bluetooth headphones, 30-hour battery life",
"price": 299.0,
"category": "Electronics",
"tags": ["Bluetooth","Noise canceling","Headphones"],
"in_stock": true,
"created_at": "2025-06-01"
}'
# Auto-generate the document ID
curl -XPOST "https://<Search engine endpoint>:9200/products/_doc" \
-u "<username>:<password>" -k \
-H "Content-Type: application/json" \
-d '{ "name": "Mechanical keyboard", "price": 499.0, "category": "Electronics" }'
Batch write (_bulk API)
The _bulk API executes multiple write operations in a single request, which significantly improves write throughput:
curl -XPOST "https://<Search engine endpoint>:9200/_bulk" \
-u "<username>:<password>" -k \
-H "Content-Type: application/json" \
-d '
{"index": {"_index": "products", "_id": "3"}}
{"name": "Smart watch", "price": 899.0, "category": "Wearables"}
{"index": {"_index": "products", "_id": "4"}}
{"name": "Power bank", "price": 129.0, "category": "Electronics"}
'
Batch write recommendations
-
Bulk size per request: 5-15 MB. Requests that are too large put memory pressure on the cluster.
-
Documents per request: 1,000 to 5,000, depending on the size of each document.
-
Concurrent write threads: 2 to 4, based on the number of CPU cores on each node.
-
During the write, set
refresh_intervalto 30s or -1, and restore it to 1s after the write completes.
Update and delete
# Partial update
curl -XPOST "https://<Search engine endpoint>:9200/products/_update/1" \
-u "<username>:<password>" -k -H "Content-Type: application/json" \
-d '{ "doc": { "price": 259.0, "in_stock": false } }'
# Delete a single document
curl -XDELETE "https://<Search engine endpoint>:9200/products/_doc/1" -u "<username>:<password>" -k
# Delete by query
curl -XPOST "https://<Search engine endpoint>:9200/products/_delete_by_query" \
-u "<username>:<password>" -k -H "Content-Type: application/json" \
-d '{ "query": { "term": { "in_stock": false } } }'
Full-text search
match query
The most common full-text search query. The query text is tokenized and then matched:
{
"query": {
"match": {
"description": {
"query": "noise canceling Bluetooth",
"operator": "and",
"minimum_should_match": "75%"
}
}
}
}
multi_match query
{
"query": {
"multi_match": {
"query": "smart watch sports",
"fields": ["name^3", "description"],
"type": "best_fields"
}
}
}
name^3 means that the name field is weighted three times higher than description.
match_phrase query
Requires the tokens after tokenization to appear in order and adjacent to each other:
{ "query": { "match_phrase": { "description": "smart watch" } } }
query_string query (Lucene syntax)
{
"query": {
"query_string": {
"query": "(noise-canceling OR Bluetooth) AND headphones",
"default_field": "description"
}
}
}
Highlighting
{
"query": { "match": { "description": "noise-canceling headphones" } },
"highlight": {
"pre_tags": ["<em>"],
"post_tags": ["</em>"],
"fields": {
"description": {
"fragment_size": 150,
"number_of_fragments": 3
}
}
}
}
Pagination
For shallow pagination (within the first 10,000 results), use from + size. For deep pagination, use search_after:
{
"size": 10,
"query": { "match_all": {} },
"sort": [ { "created_at": "desc" }, { "_id": "asc" } ],
"search_after": ["2025-05-25", "4"]
}
Avoid using from + size for deep pagination (for example, from=10000), which causes severe performance issues. Use search_after instead.
Exact queries and filters
// term
{ "query": { "term": { "category": "Electronics" } } }
// terms
{ "query": { "terms": { "category": ["Electronics","Wearables"] } } }
// range
{ "query": { "range": { "price": { "gte": 100, "lte": 500 } } } }
// Date range
{ "query": { "range": { "created_at": { "gte": "2025-05-01", "lt": "2025-06-01" } } } }
// exists
{ "query": { "exists": { "field": "tags" } } }
Bool compound query
{
"query": {
"bool": {
"must": [ { "match": { "description": "headphones" } } ],
"filter": [
{ "term": { "in_stock": true } },
{ "range": { "price": { "lte": 500 } } }
],
"should": [ { "term": { "tags": "Noise canceling" } } ],
"must_not": [ { "term": { "category": "Wearables" } } ]
}
}
}
|
Clause |
Description |
Participates in scoring |
|
|
Must match. |
Yes |
|
|
Must match but does not participate in scoring. |
No (uses cache for better performance). |
|
|
Matches at least one clause to add to the score. |
Yes |
|
|
Must not match. |
No |
Place filter conditions that do not need scoring (such as statuses and date ranges) in filter. This uses the cache to improve performance.
Vector search (KNN)
Basic KNN query
{
"size": 5,
"query": {
"knn": {
"embedding": {
"vector": [0.1, 0.2, 0.3, "..."],
"k": 5
}
}
}
}
Vector search with filter conditions
{
"size": 5,
"query": {
"knn": {
"embedding": {
"vector": [0.1, 0.2, 0.3, "..."],
"k": 5,
"filter": {
"bool": {
"must": [
{ "term": { "category": "Technical documentation" } },
{ "range": { "created_at": { "gte": "2025-01-01" } } }
]
}
}
}
}
}
}
Vector + full-text hybrid search
{
"size": 10,
"query": {
"bool": {
"should": [
{ "match": { "content": { "query": "distributed database architecture", "boost": 0.3 } } },
{ "knn": { "embedding": { "vector": [0.1, 0.2, 0.3, "..."], "k": 10 } } }
]
}
}
}
Aggregation analysis
// Metric aggregation
{
"size": 0,
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"max_price": { "max": { "field": "price" } },
"min_price": { "min": { "field": "price" } },
"price_stats": { "stats": { "field": "price" } }
}
}
// Terms grouping aggregation (nested sub-aggregation)
{
"size": 0,
"aggs": {
"by_category": {
"terms": { "field": "category", "size": 10, "order": { "_count": "desc" } },
"aggs": { "avg_price": { "avg": { "field": "price" } } }
}
}
}
// Date Histogram
{
"size": 0,
"aggs": {
"products_over_time": {
"date_histogram": { "field": "created_at", "calendar_interval": "month", "format": "yyyy-MM" }
}
}
}
Geospatial query
// geo_distance
{
"query": {
"geo_distance": {
"distance": "5km",
"location": { "lat": 31.23, "lon": 121.47 }
}
},
"sort": [
{
"_geo_distance": {
"location": { "lat": 31.23, "lon": 121.47 },
"order": "asc",
"unit": "km"
}
}
]
}
// geo_bounding_box
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": { "lat": 31.3, "lon": 121.4 },
"bottom_right": { "lat": 31.1, "lon": 121.6 }
}
}
}
}