Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/changelog/130638.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pr: 130638
summary: Prevent search functions work with a non-STANDARD index
area: ES|QL
type: bug
issues:
- 130561
- 129778
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
package org.elasticsearch.xpack.esql.plugin;

import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.client.internal.IndicesAdminClient;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xpack.esql.EsqlTestUtils;
import org.elasticsearch.xpack.esql.VerificationException;
import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
import org.junit.Before;
Expand All @@ -25,7 +28,9 @@
import java.util.Locale;
import java.util.Map;

import static org.elasticsearch.index.IndexMode.LOOKUP;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.CoreMatchers.containsString;

public class KnnFunctionIT extends AbstractEsqlIntegTestCase {

Expand Down Expand Up @@ -109,6 +114,26 @@ public void testKnnNonPushedDown() {
}
}

public void testKnnWithLookupJoin() {
float[] queryVector = new float[numDims];
Arrays.fill(queryVector, 1.0f);

var query = String.format(Locale.ROOT, """
FROM test
| LOOKUP JOIN test_lookup ON id
| WHERE KNN(lookup_vector, %s, {"k": 5}) OR id > 10
""", Arrays.toString(queryVector));

var error = expectThrows(VerificationException.class, () -> run(query));
assertThat(
error.getMessage(),
containsString(
"line 3:13: [KNN] function cannot operate on [lookup_vector], supplied by an index [test_lookup] in non-STANDARD "
+ "mode [lookup]"
)
);
}

@Before
public void setup() throws IOException {
assumeTrue("Needs KNN support", EsqlCapabilities.Cap.KNN_FUNCTION.isEnabled());
Expand Down Expand Up @@ -152,5 +177,31 @@ public void setup() throws IOException {
}

indexRandom(true, docs);

createAndPopulateLookupIndex(client, "test_lookup");
}

private void createAndPopulateLookupIndex(IndicesAdminClient client, String lookupIndexName) throws IOException {
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()
.startObject("properties")
.startObject("id")
.field("type", "integer")
.endObject()
.startObject("lookup_vector")
.field("type", "dense_vector")
.field("similarity", "l2_norm")
.endObject()
.endObject()
.endObject();

Settings.Builder settingsBuilder = Settings.builder()
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexSettings.MODE.getKey(), LOOKUP.getName());

var createRequest = client.prepareCreate(lookupIndexName).setMapping(mapping).setSettings(settingsBuilder.build());
assertAcked(createRequest);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.internal.IndicesAdminClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.esql.VerificationException;
import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
import org.hamcrest.Matchers;
import org.junit.Before;

import java.util.List;
import java.util.function.Consumer;

import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.getValuesList;
Expand All @@ -27,7 +29,7 @@ public class MatchFunctionIT extends AbstractEsqlIntegTestCase {

@Before
public void setupIndex() {
createAndPopulateIndex();
createAndPopulateIndex(this::ensureYellow);
}

public void testSimpleWhereMatch() {
Expand Down Expand Up @@ -294,13 +296,30 @@ public void testMatchWithinEval() {
assertThat(error.getMessage(), containsString("[MATCH] function is only supported in WHERE and STATS commands"));
}

private void createAndPopulateIndex() {
public void testMatchWithLookupJoin() {
var query = """
FROM test
| LOOKUP JOIN test_lookup ON id
| WHERE id > 0 AND MATCH(lookup_content, "fox")
""";

var error = expectThrows(VerificationException.class, () -> run(query));
assertThat(
error.getMessage(),
containsString(
"line 3:26: [MATCH] function cannot operate on [lookup_content], supplied by an index [test_lookup] "
+ "in non-STANDARD mode [lookup]"
)
);
}

static void createAndPopulateIndex(Consumer<String[]> ensureYellow) {
var indexName = "test";
var client = client().admin().indices();
var CreateRequest = client.prepareCreate(indexName)
var createRequest = client.prepareCreate(indexName)
.setSettings(Settings.builder().put("index.number_of_shards", 1))
.setMapping("id", "type=integer", "content", "type=text");
assertAcked(CreateRequest);
assertAcked(createRequest);
client().prepareBulk()
.add(new IndexRequest(indexName).id("1").source("id", 1, "content", "This is a brown fox"))
.add(new IndexRequest(indexName).id("2").source("id", 2, "content", "This is a brown dog"))
Expand All @@ -310,6 +329,17 @@ private void createAndPopulateIndex() {
.add(new IndexRequest(indexName).id("6").source("id", 6, "content", "The quick brown fox jumps over the lazy dog"))
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
ensureYellow(indexName);

var lookupIndexName = "test_lookup";
createAndPopulateLookupIndex(client, lookupIndexName);

ensureYellow.accept(new String[] { indexName, lookupIndexName });
}

static void createAndPopulateLookupIndex(IndicesAdminClient client, String lookupIndexName) {
var createRequest = client.prepareCreate(lookupIndexName)
.setSettings(Settings.builder().put("index.number_of_shards", 1).put("index.mode", "lookup"))
.setMapping("id", "type=integer", "lookup_content", "type=text");
assertAcked(createRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
package org.elasticsearch.xpack.esql.plugin;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.xpack.esql.VerificationException;
Expand All @@ -21,7 +18,6 @@

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.xpack.esql.action.EsqlQueryRequest.syncEsqlQueryRequest;
import static org.hamcrest.CoreMatchers.containsString;

Expand All @@ -30,7 +26,7 @@ public class MatchOperatorIT extends AbstractEsqlIntegTestCase {

@Before
public void setupIndex() {
createAndPopulateIndex();
MatchFunctionIT.createAndPopulateIndex(this::ensureYellow);
}

public void testSimpleWhereMatch() {
Expand Down Expand Up @@ -372,22 +368,20 @@ public void testMatchWithNonTextField() {
}
}

private void createAndPopulateIndex() {
var indexName = "test";
var client = client().admin().indices();
var CreateRequest = client.prepareCreate(indexName)
.setSettings(Settings.builder().put("index.number_of_shards", 1))
.setMapping("id", "type=integer", "content", "type=text");
assertAcked(CreateRequest);
client().prepareBulk()
.add(new IndexRequest(indexName).id("1").source("id", 1, "content", "This is a brown fox"))
.add(new IndexRequest(indexName).id("2").source("id", 2, "content", "This is a brown dog"))
.add(new IndexRequest(indexName).id("3").source("id", 3, "content", "This dog is really brown"))
.add(new IndexRequest(indexName).id("4").source("id", 4, "content", "The dog is brown but this document is very very long"))
.add(new IndexRequest(indexName).id("5").source("id", 5, "content", "There is also a white cat"))
.add(new IndexRequest(indexName).id("6").source("id", 6, "content", "The quick brown fox jumps over the lazy dog"))
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
ensureYellow(indexName);
public void testMatchOperatorWithLookupJoin() {
var query = """
FROM test
| LOOKUP JOIN test_lookup ON id
| WHERE id > 0 AND lookup_content : "fox"
""";

var error = expectThrows(VerificationException.class, () -> run(query));
assertThat(
error.getMessage(),
containsString(
"line 3:20: [:] operator cannot operate on [lookup_content], supplied by an index [test_lookup] "
+ "in non-STANDARD mode [lookup]"
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
package org.elasticsearch.xpack.esql.plugin;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.esql.VerificationException;
import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
import org.hamcrest.Matchers;
Expand All @@ -19,16 +16,16 @@
import java.util.Collections;
import java.util.List;

import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.getValuesList;
import static org.elasticsearch.xpack.esql.plugin.MatchFunctionIT.createAndPopulateIndex;
import static org.hamcrest.CoreMatchers.containsString;

//@TestLogging(value = "org.elasticsearch.xpack.esql:TRACE,org.elasticsearch.compute:TRACE", reason = "debug")
public class MatchPhraseFunctionIT extends AbstractEsqlIntegTestCase {

@Before
public void setupIndex() {
createAndPopulateIndex();
createAndPopulateIndex(this::ensureYellow);
}

public void testSimpleWhereMatchPhrase() {
Expand Down Expand Up @@ -325,22 +322,20 @@ public void testMatchPhraseWithinEval() {
assertThat(error.getMessage(), containsString("[MatchPhrase] function is only supported in WHERE and STATS commands"));
}

private void createAndPopulateIndex() {
var indexName = "test";
var client = client().admin().indices();
var CreateRequest = client.prepareCreate(indexName)
.setSettings(Settings.builder().put("index.number_of_shards", 1))
.setMapping("id", "type=integer", "content", "type=text");
assertAcked(CreateRequest);
client().prepareBulk()
.add(new IndexRequest(indexName).id("1").source("id", 1, "content", "This is a brown fox"))
.add(new IndexRequest(indexName).id("2").source("id", 2, "content", "This is a brown dog"))
.add(new IndexRequest(indexName).id("3").source("id", 3, "content", "This dog is really brown"))
.add(new IndexRequest(indexName).id("4").source("id", 4, "content", "The dog is brown but this document is very very long"))
.add(new IndexRequest(indexName).id("5").source("id", 5, "content", "There is also a white cat"))
.add(new IndexRequest(indexName).id("6").source("id", 6, "content", "The quick brown fox jumps over the lazy dog"))
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
ensureYellow(indexName);
public void testMatchPhraseWithLookupJoin() {
var query = """
FROM test
| LOOKUP JOIN test_lookup ON id
| WHERE id > 0 AND MATCH_PHRASE(lookup_content, "fox")
""";

var error = expectThrows(VerificationException.class, () -> run(query));
assertThat(
error.getMessage(),
containsString(
"line 3:33: [MatchPhrase] function cannot operate on [lookup_content], supplied by an index [test_lookup] "
+ "in non-STANDARD mode [lookup]"
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
import org.junit.Before;

import java.util.List;
import java.util.function.Consumer;

import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.xpack.esql.plugin.MatchFunctionIT.createAndPopulateLookupIndex;
import static org.hamcrest.CoreMatchers.containsString;

public class QueryStringIT extends AbstractEsqlIntegTestCase {

@Before
public void setupIndex() {
createAndPopulateIndex();
createAndPopulateIndex(this::ensureYellow);
}

public void testSimpleQueryString() {
Expand Down Expand Up @@ -91,7 +93,7 @@ public void testInvalidQueryStringLexicalError() {
);
}

private void createAndPopulateIndex() {
static void createAndPopulateIndex(Consumer<String[]> ensureYellow) {
var indexName = "test";
var client = client().admin().indices();
var CreateRequest = client.prepareCreate(indexName)
Expand Down Expand Up @@ -135,7 +137,11 @@ private void createAndPopulateIndex() {
)
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
ensureYellow(indexName);

var lookupIndexName = "test_lookup";
createAndPopulateLookupIndex(client, lookupIndexName);

ensureYellow.accept(new String[] { indexName, lookupIndexName });
}

public void testWhereQstrWithScoring() {
Expand Down Expand Up @@ -228,4 +234,15 @@ AND abs(id) > 0
assertValuesInAnyOrder(resp.values(), List.of(List.of(5, 1.0), List.of(4, 1.0)));
}
}

public void testWhereQstrWithLookupJoin() {
var query = """
FROM test
| LOOKUP JOIN test_lookup ON id
| WHERE id > 0 AND QSTR("lookup_content: fox")
""";

var error = expectThrows(VerificationException.class, () -> run(query));
assertThat(error.getMessage(), containsString("line 3:3: [QSTR] function cannot be used after LOOKUP"));
}
}
Loading
Loading