|
118 | 118 | import static org.elasticsearch.index.query.QueryBuilders.boolQuery; |
119 | 119 | import static org.elasticsearch.index.query.QueryBuilders.existsQuery; |
120 | 120 | import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; |
| 121 | +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; |
121 | 122 | import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; |
122 | 123 | import static org.elasticsearch.index.query.QueryBuilders.termQuery; |
123 | 124 | import static org.elasticsearch.index.query.QueryBuilders.termsQuery; |
@@ -2072,6 +2073,108 @@ public void testKnnWithoutExplicitLimit() { |
2072 | 2073 | assertThat(knnQuery.k(), is(config.resultTruncationDefaultSize())); |
2073 | 2074 | } |
2074 | 2075 |
|
| 2076 | + public void testKnnWithExplicitLimit() { |
| 2077 | + var query = """ |
| 2078 | + from test |
| 2079 | + | where knn(dense_vector, [0, 1, 2]) |
| 2080 | + | limit 10 |
| 2081 | + """; |
| 2082 | + var analyzer = makeAnalyzer("mapping-all-types.json"); |
| 2083 | + var plan = plannerOptimizer.plan(query, IS_SV_STATS, analyzer); |
| 2084 | + var limitExec = as(plan, LimitExec.class); |
| 2085 | + assertThat(limitExec.limit().fold(FoldContext.small()), is(10)); |
| 2086 | + var exchangeExec = as(limitExec.child(), ExchangeExec.class); |
| 2087 | + var projectExec = as(exchangeExec.child(), ProjectExec.class); |
| 2088 | + var fieldExtractExec = as(projectExec.child(), FieldExtractExec.class); |
| 2089 | + var queryExec = as(fieldExtractExec.child(), EsQueryExec.class); |
| 2090 | + assertThat(queryExec.limit().fold(FoldContext.small()), is(10)); |
| 2091 | + var knnQuery = as(queryExec.query(), KnnVectorQueryBuilder.class); |
| 2092 | + assertThat(knnQuery.k(), is(10)); |
| 2093 | + } |
| 2094 | + |
| 2095 | + public void testMultipleKnnQueriesLimit() { |
| 2096 | + var query = """ |
| 2097 | + from test |
| 2098 | + | where knn(dense_vector, [0, 1, 2]) and (match(text, "example") or knn(dense_vector, [3, 4, 5])) |
| 2099 | + | limit 10 |
| 2100 | + """; |
| 2101 | + var analyzer = makeAnalyzer("mapping-all-types.json"); |
| 2102 | + var plan = plannerOptimizer.plan(query, IS_SV_STATS, analyzer); |
| 2103 | + var limitExec = as(plan, LimitExec.class); |
| 2104 | + assertThat(limitExec.limit().fold(FoldContext.small()), is(10)); |
| 2105 | + var exchangeExec = as(limitExec.child(), ExchangeExec.class); |
| 2106 | + var projectExec = as(exchangeExec.child(), ProjectExec.class); |
| 2107 | + var fieldExtractExec = as(projectExec.child(), FieldExtractExec.class); |
| 2108 | + var queryExec = as(fieldExtractExec.child(), EsQueryExec.class); |
| 2109 | + assertThat(queryExec.limit().fold(FoldContext.small()), is(10)); |
| 2110 | + var expectedQuery = boolQuery().must(new KnnVectorQueryBuilder("dense_vector", new float[] { 0f, 1f, 2f }, 10, null, null, null)) |
| 2111 | + .must( |
| 2112 | + boolQuery().should(matchQuery("text", "example").lenient(true)) |
| 2113 | + .should(new KnnVectorQueryBuilder("dense_vector", new float[] { 3f, 4f, 5f }, 10, null, null, null)) |
| 2114 | + ); |
| 2115 | + assertEquals(expectedQuery.toString(), queryExec.query().toString()); |
| 2116 | + |
| 2117 | + query = """ |
| 2118 | + from test |
| 2119 | + | where knn(dense_vector, [0, 1, 2]) |
| 2120 | + | where (match(text, "example") or knn(dense_vector, [3, 4, 5])) |
| 2121 | + | limit 10 |
| 2122 | + """; |
| 2123 | + analyzer = makeAnalyzer("mapping-all-types.json"); |
| 2124 | + plan = plannerOptimizer.plan(query, IS_SV_STATS, analyzer); |
| 2125 | + limitExec = as(plan, LimitExec.class); |
| 2126 | + assertThat(limitExec.limit().fold(FoldContext.small()), is(10)); |
| 2127 | + exchangeExec = as(limitExec.child(), ExchangeExec.class); |
| 2128 | + projectExec = as(exchangeExec.child(), ProjectExec.class); |
| 2129 | + fieldExtractExec = as(projectExec.child(), FieldExtractExec.class); |
| 2130 | + queryExec = as(fieldExtractExec.child(), EsQueryExec.class); |
| 2131 | + assertThat(queryExec.limit().fold(FoldContext.small()), is(10)); |
| 2132 | + expectedQuery = boolQuery().must(new KnnVectorQueryBuilder("dense_vector", new float[] { 0f, 1f, 2f }, 10, null, null, null)) |
| 2133 | + .must( |
| 2134 | + boolQuery().should(matchQuery("text", "example").lenient(true)) |
| 2135 | + .should(new KnnVectorQueryBuilder("dense_vector", new float[] { 3f, 4f, 5f }, 10, null, null, null)) |
| 2136 | + ); |
| 2137 | + assertEquals(expectedQuery.toString(), queryExec.query().toString()); |
| 2138 | + } |
| 2139 | + |
| 2140 | + public void testKnnWithCombinedLimits() { |
| 2141 | + var query = """ |
| 2142 | + from test |
| 2143 | + | where knn(dense_vector, [0, 1, 2]) |
| 2144 | + | limit 20 |
| 2145 | + | limit 10 |
| 2146 | + """; |
| 2147 | + var analyzer = makeAnalyzer("mapping-all-types.json"); |
| 2148 | + var plan = plannerOptimizer.plan(query, IS_SV_STATS, analyzer); |
| 2149 | + var limitExec = as(plan, LimitExec.class); |
| 2150 | + assertThat(limitExec.limit().fold(FoldContext.small()), is(10)); |
| 2151 | + var exchangeExec = as(limitExec.child(), ExchangeExec.class); |
| 2152 | + var projectExec = as(exchangeExec.child(), ProjectExec.class); |
| 2153 | + var fieldExtractExec = as(projectExec.child(), FieldExtractExec.class); |
| 2154 | + var queryExec = as(fieldExtractExec.child(), EsQueryExec.class); |
| 2155 | + assertThat(queryExec.limit().fold(FoldContext.small()), is(10)); |
| 2156 | + var knnQuery = as(queryExec.query(), KnnVectorQueryBuilder.class); |
| 2157 | + assertThat(knnQuery.k(), is(10)); |
| 2158 | + |
| 2159 | + query = """ |
| 2160 | + from test |
| 2161 | + | where knn(dense_vector, [0, 1, 2]) |
| 2162 | + | limit 10 |
| 2163 | + | limit 20 |
| 2164 | + """; |
| 2165 | + analyzer = makeAnalyzer("mapping-all-types.json"); |
| 2166 | + plan = plannerOptimizer.plan(query, IS_SV_STATS, analyzer); |
| 2167 | + limitExec = as(plan, LimitExec.class); |
| 2168 | + assertThat(limitExec.limit().fold(FoldContext.small()), is(10)); |
| 2169 | + exchangeExec = as(limitExec.child(), ExchangeExec.class); |
| 2170 | + projectExec = as(exchangeExec.child(), ProjectExec.class); |
| 2171 | + fieldExtractExec = as(projectExec.child(), FieldExtractExec.class); |
| 2172 | + queryExec = as(fieldExtractExec.child(), EsQueryExec.class); |
| 2173 | + assertThat(queryExec.limit().fold(FoldContext.small()), is(10)); |
| 2174 | + knnQuery = as(queryExec.query(), KnnVectorQueryBuilder.class); |
| 2175 | + assertThat(knnQuery.k(), is(10)); |
| 2176 | + } |
| 2177 | + |
2075 | 2178 | private boolean isMultiTypeEsField(Expression e) { |
2076 | 2179 | return e instanceof FieldAttribute fa && fa.field() instanceof MultiTypeEsField; |
2077 | 2180 | } |
|
0 commit comments