diff --git a/docs/content/docs/development/vector_stores.md b/docs/content/docs/development/vector_stores.md
index 07d5eafa7..f4462a071 100644
--- a/docs/content/docs/development/vector_stores.md
+++ b/docs/content/docs/development/vector_stores.md
@@ -800,7 +800,7 @@ Elasticsearch is currently supported in the Java API only. To use Elasticsearch
| `dims` | int | `768` | Vector dimensionality |
| `k` | int | None | Number of nearest neighbors to return; can be overridden per query |
| `num_candidates` | int | None | Candidate set size for ANN search; can be overridden per query |
-| `filter_query` | str | None | Raw JSON Elasticsearch filter query (DSL) applied as a post-filter |
+| `filter_query` | str | None | Raw JSON Elasticsearch filter query (DSL) restricting which documents a KNN query can match |
| `host` | str | `"http://localhost:9200"` | Elasticsearch endpoint |
| `hosts` | str | None | Comma-separated list of Elasticsearch endpoints |
| `username` | str | None | Username for basic authentication |
diff --git a/integrations/vector-stores/elasticsearch/src/main/java/org/apache/flink/agents/integrations/vectorstores/elasticsearch/ElasticsearchVectorStore.java b/integrations/vector-stores/elasticsearch/src/main/java/org/apache/flink/agents/integrations/vectorstores/elasticsearch/ElasticsearchVectorStore.java
index 424c9d398..5d66e479f 100644
--- a/integrations/vector-stores/elasticsearch/src/main/java/org/apache/flink/agents/integrations/vectorstores/elasticsearch/ElasticsearchVectorStore.java
+++ b/integrations/vector-stores/elasticsearch/src/main/java/org/apache/flink/agents/integrations/vectorstores/elasticsearch/ElasticsearchVectorStore.java
@@ -82,8 +82,8 @@
*
Authentication (optional): Either basic auth via {@code username}/{@code password}, or API
@@ -572,7 +572,9 @@ private void deleteDocuments(
*
* The method prepares a KNN search request using the supplied {@code embedding} and merges
* default arguments from the store with the provided {@code args}. Optional filter queries
- * (JSON DSL) are applied as a post filter.
+ * (JSON DSL) restrict the documents the KNN search may match, so the nearest neighbours are
+ * selected from among the matching documents rather than filtered out afterwards. Up to {@code
+ * k} matching documents are returned even when the closest vectors overall do not match.
*
* @param embedding The embedding vector to search with
* @param limit Maximum number of items the caller is interested in; used as a fallback for
@@ -603,20 +605,32 @@ public List queryEmbedding(
List queryVector = new ArrayList<>(embedding.length);
for (float v : embedding) queryVector.add(v);
+ final String finalCombined = combined;
SearchRequest.Builder builder =
new SearchRequest.Builder()
.index(index)
.knn(
- kb ->
- kb.field(this.vectorField)
- .queryVector(queryVector)
- .k(k)
- .numCandidates(numCandidates));
-
- if (combined != null) {
- final String finalCombined = combined;
- builder = builder.postFilter(f -> f.withJson(new StringReader(finalCombined)));
- }
+ kb -> {
+ kb.field(this.vectorField)
+ .queryVector(queryVector)
+ .k(k)
+ .numCandidates(numCandidates);
+ // Filter inside the KNN clause rather than after it, so the
+ // k nearest neighbours are chosen from the documents that
+ // match. A post-filter can only discard hits the vector
+ // search already picked, which yields fewer than k results
+ // whenever the nearest vectors belong to filtered-out
+ // documents.
+ if (finalCombined != null) {
+ kb.filter(
+ f ->
+ f.withJson(
+ new StringReader(
+ finalCombined)));
+ }
+ return kb;
+ });
+
final SearchResponse