Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
fa3dd20
TEMP
GalLalouche Jan 13, 2025
c2b0d9c
WTF checkstyle?!
GalLalouche Jan 9, 2025
7b33f15
Trying to fix failures
GalLalouche Jan 9, 2025
a105f0a
Add capability, Fix parser test
GalLalouche Jan 12, 2025
0ca5e79
Update docs/changelog/119886.yaml
GalLalouche Jan 12, 2025
bf309fb
Update changelog
GalLalouche Jan 14, 2025
0b72c89
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 14, 2025
c046112
Really merge main this time
GalLalouche Jan 14, 2025
aca3796
Merge branch 'main' into feature/unmapped_fields_squashed
craigtaverner Jan 14, 2025
dacc8f6
Craig's PR notes
GalLalouche Jan 14, 2025
f71a586
Test for no sources
GalLalouche Jan 16, 2025
2ba5935
More code review fixes
GalLalouche Jan 16, 2025
c22b9d4
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 16, 2025
f5c77fa
Update docs/changelog/119886.yaml
GalLalouche Jan 19, 2025
d6afb24
More code review fixes
GalLalouche Jan 19, 2025
25f0c53
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 19, 2025
165ad16
Merge branch 'feature/unmapped_fields_squashed' of github.com:GalLalo…
GalLalouche Jan 19, 2025
9d85915
Some of Costin's notes
GalLalouche Jan 22, 2025
d0959ea
Pre huge refactor
GalLalouche Jan 23, 2025
d68a0ca
Post huge refactor
GalLalouche Jan 23, 2025
a12aa82
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 26, 2025
f02fd43
Temp fixes
GalLalouche Jan 26, 2025
a8d9372
Slighly more delicate refactor
GalLalouche Jan 26, 2025
fcd4663
Fix borken test
GalLalouche Jan 26, 2025
be4bbfd
Handle failing tests
GalLalouche Jan 26, 2025
b84ff2c
Fix test names
GalLalouche Jan 26, 2025
a0e1840
Replace flag with inheritance
GalLalouche Jan 26, 2025
0b4132e
Add comment parsing
GalLalouche Jan 27, 2025
b05b327
Add a few more unmapped fields CSV tests
GalLalouche Jan 29, 2025
3f9faef
Added IndexResolverFieldNames test
GalLalouche Jan 29, 2025
903f6fa
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 29, 2025
85b84f5
Move tests from LogicalPlanOptimizerTests to AnalyzerTests
GalLalouche Jan 29, 2025
400f473
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 29, 2025
fb09120
Properly apply Andrei's suggestion after it somehow got removed o_O
GalLalouche Jan 29, 2025
0d9b280
Add snapshot check to StatementParserTests
GalLalouche Jan 29, 2025
d7ce623
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 29, 2025
0f01ebe
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Jan 30, 2025
5c2c7d7
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 2, 2025
af26d5f
More code review fixes
GalLalouche Feb 2, 2025
faef67a
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 2, 2025
1204791
TEMP
GalLalouche Feb 2, 2025
22569b8
Things pass! Excitement!
GalLalouche Feb 4, 2025
a9f4a0b
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 5, 2025
8f3c235
Remove println
GalLalouche Feb 5, 2025
7d07d1a
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 5, 2025
697989c
Fix borken test
GalLalouche Feb 5, 2025
c0fe85e
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 5, 2025
ab9bfdf
More review fixes
GalLalouche Feb 6, 2025
5c1c2be
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 6, 2025
9805f12
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 9, 2025
d5dc662
Fix borken test
GalLalouche Feb 9, 2025
ef56e8a
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 10, 2025
13b3dff
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 10, 2025
088a622
Try to fix BWC test
GalLalouche Feb 11, 2025
ea8567b
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 11, 2025
9c466e4
Try to fix BWC test (2)
GalLalouche Feb 11, 2025
1fb5cb4
Try to fix BWC test (3)
GalLalouche Feb 11, 2025
8017c78
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 11, 2025
4f6f6a1
Try to fix BWC test (4)
GalLalouche Feb 11, 2025
971b869
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 11, 2025
ef94c52
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 12, 2025
c9fdc92
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 12, 2025
b46cb9d
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 12, 2025
b5da964
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 12, 2025
7a48277
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 12, 2025
dcc518c
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 13, 2025
770ea6f
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 13, 2025
97399fc
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 13, 2025
e32a446
Merge branch 'main' into feature/unmapped_fields_squashed
GalLalouche Feb 13, 2025
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
5 changes: 5 additions & 0 deletions docs/changelog/119886.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 119886
summary: Initial support for unmapped fields
area: ES|QL
type: feature
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* Loads values from {@code _source}. This whole process is very slow and cast-tastic,
Expand Down Expand Up @@ -230,7 +231,7 @@ private static class BytesRefs extends BlockSourceReader {

@Override
protected void append(BlockLoader.Builder builder, Object v) {
((BlockLoader.BytesRefBuilder) builder).appendBytesRef(toBytesRef(scratch, (String) v));
((BlockLoader.BytesRefBuilder) builder).appendBytesRef(toBytesRef(scratch, Objects.toString(v)));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
public class EsField implements Writeable {

private static Map<String, Writeable.Reader<? extends EsField>> readers = Map.ofEntries(
Map.entry("EsField", EsField::new),
Map.entry("DateEsField", DateEsField::new),
Map.entry("EsField", EsField::new),
Map.entry("InvalidMappedField", InvalidMappedField::new),
Map.entry("KeywordEsField", KeywordEsField::new),
Map.entry("MultiTypeEsField", MultiTypeEsField::new),
Map.entry("PotentiallyUnmappedKeywordEsField", PotentiallyUnmappedKeywordEsField::new),
Map.entry("TextEsField", TextEsField::new),
Map.entry("UnsupportedEsField", UnsupportedEsField::new)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public InvalidMappedField(String name, String errorMessage) {
* Constructor supporting union types, used in ES|QL.
*/
public InvalidMappedField(String name, Map<String, Set<String>> typesToIndices) {
this(name, makeErrorMessage(typesToIndices), new TreeMap<>(), typesToIndices);
this(name, makeErrorMessage(typesToIndices, false), new TreeMap<>(), typesToIndices);
}

private InvalidMappedField(String name, String errorMessage, Map<String, EsField> properties, Map<String, Set<String>> typesToIndices) {
Expand Down Expand Up @@ -107,12 +107,21 @@ public Map<String, Set<String>> getTypesToIndices() {
return typesToIndices;
}

private static String makeErrorMessage(Map<String, Set<String>> typesToIndices) {
public static String makeErrorsMessageIncludingInsistKeyword(Map<String, Set<String>> typesToIndices) {
return makeErrorMessage(typesToIndices, true);
}

private static String makeErrorMessage(Map<String, Set<String>> typesToIndices, boolean includeInsistKeyword) {
StringBuilder errorMessage = new StringBuilder();
var isInsistKeywordOnlyKeyword = includeInsistKeyword && typesToIndices.containsKey(DataType.KEYWORD.typeName()) == false;
errorMessage.append("mapped as [");
errorMessage.append(typesToIndices.size());
errorMessage.append(typesToIndices.size() + (isInsistKeywordOnlyKeyword ? 1 : 0));
errorMessage.append("] incompatible types: ");
boolean first = true;
if (isInsistKeywordOnlyKeyword) {
first = false;
errorMessage.append("[keyword] enforced by INSIST command");
}
for (Map.Entry<String, Set<String>> e : typesToIndices.entrySet()) {
if (first) {
first = false;
Expand All @@ -121,7 +130,12 @@ private static String makeErrorMessage(Map<String, Set<String>> typesToIndices)
}
errorMessage.append("[");
errorMessage.append(e.getKey());
errorMessage.append("] in ");
errorMessage.append("] ");
if (e.getKey().equals(DataType.KEYWORD.typeName()) && includeInsistKeyword) {
errorMessage.append("enforced by INSIST command and in ");
} else {
errorMessage.append("in ");
}
if (e.getValue().size() <= 3) {
errorMessage.append(e.getValue());
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.esql.core.type;

import org.elasticsearch.common.io.stream.StreamInput;

import java.io.IOException;

/**
* This class is used as a marker for fields that may be unmapped, where an unmapped field is a field which exists in the _source but is not
* mapped in the index. Note that this field may be mapped for some indices, but is unmapped in at least one of them.
* For indices where the field is unmapped, we will try to load them directly from _source.
*/
public class PotentiallyUnmappedKeywordEsField extends KeywordEsField {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field will be unmapped in at least one index hence its potential has been realized :) and the class can be simply called UnmappedKeywordEsField

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason it is "potentially" unmapped is that it might be mapped in some fields. I've added a comment to try to better explain this.

public PotentiallyUnmappedKeywordEsField(String name) {
super(name);
}

public PotentiallyUnmappedKeywordEsField(StreamInput in) throws IOException {
super(in);
}

public String getWriteableName() {
return "PotentiallyUnmappedKeywordEsField";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import static org.elasticsearch.xpack.esql.CsvTestUtils.isEnabled;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V12;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.SOURCE_FIELD_MAPPING;

public class MixedClusterEsqlSpecIT extends EsqlSpecTestCase {
@ClassRule
Expand Down Expand Up @@ -90,6 +91,11 @@ protected boolean supportsIndexModeLookup() throws IOException {
return hasCapabilities(List.of(JOIN_LOOKUP_V12.capabilityName()));
}

@Override
protected boolean supportsSourceFieldMapping() throws IOException {
return hasCapabilities(List.of(SOURCE_FIELD_MAPPING.capabilityName()));
}

@Override
protected boolean deduplicateExactWarnings() {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V12;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_PLANNING_V1;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.METADATA_FIELDS_REMOTE_TEST;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.UNMAPPED_FIELDS;
import static org.elasticsearch.xpack.esql.qa.rest.EsqlSpecTestCase.Mode.SYNC;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
Expand Down Expand Up @@ -127,6 +128,8 @@ protected void shouldSkipTest(String testName) throws IOException {
assumeFalse("INLINESTATS not yet supported in CCS", testCase.requiredCapabilities.contains(JOIN_PLANNING_V1.capabilityName()));
assumeFalse("INLINESTATS not yet supported in CCS", testCase.requiredCapabilities.contains(INLINESTATS_V3.capabilityName()));
assumeFalse("LOOKUP JOIN not yet supported in CCS", testCase.requiredCapabilities.contains(JOIN_LOOKUP_V12.capabilityName()));
// Unmapped fields require a coorect capability response from every cluster, which isn't currently implemented.
assumeFalse("UNMAPPED FIELDS not yet supported in CCS", testCase.requiredCapabilities.contains(UNMAPPED_FIELDS.capabilityName()));
}

@Override
Expand Down Expand Up @@ -294,4 +297,9 @@ protected boolean supportsIndexModeLookup() throws IOException {
// return hasCapabilities(List.of(JOIN_LOOKUP_V10.capabilityName()));
return false;
}

@Override
protected boolean supportsSourceFieldMapping() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import org.elasticsearch.xpack.esql.qa.rest.EsqlSpecTestCase;
import org.junit.ClassRule;

import java.io.IOException;

public class EsqlSpecIT extends EsqlSpecTestCase {
@ClassRule
public static ElasticsearchCluster cluster = Clusters.testCluster(spec -> spec.plugin("inference-service-test"));
Expand Down Expand Up @@ -39,7 +41,7 @@ protected boolean enableRoundingDoubleValuesOnAsserting() {
}

@Override
protected boolean shouldSkipTestsWithSemanticTextFields() {
return true;
protected boolean supportsSourceFieldMapping() throws IOException {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,9 @@ protected boolean enableRoundingDoubleValuesOnAsserting() {
protected boolean shouldSkipTestsWithSemanticTextFields() {
return cluster.getNumNodes() > 1;
}

@Override
protected boolean supportsSourceFieldMapping() {
return cluster.getNumNodes() == 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import static org.elasticsearch.xpack.esql.CsvTestsDataLoader.loadDataSetIntoEs;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.classpathResources;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.SEMANTIC_TEXT_TYPE;
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.SOURCE_FIELD_MAPPING;

// This test can run very long in serverless configurations
@TimeoutSuite(millis = 30 * TimeUnits.MINUTE)
Expand Down Expand Up @@ -135,8 +136,11 @@ public void setup() throws IOException {
if (supportsInferenceTestService() && clusterHasInferenceEndpoint(client()) == false) {
createInferenceEndpoint(client());
}
if (indexExists(availableDatasetsForEs(client(), supportsIndexModeLookup()).iterator().next().indexName()) == false) {
loadDataSetIntoEs(client(), supportsIndexModeLookup());

boolean supportsLookup = supportsIndexModeLookup();
boolean supportsSourceMapping = supportsSourceFieldMapping();
if (indexExists(availableDatasetsForEs(client(), supportsLookup, supportsSourceMapping).iterator().next().indexName()) == false) {
loadDataSetIntoEs(client(), supportsLookup, supportsSourceMapping);
}
}

Expand Down Expand Up @@ -183,6 +187,9 @@ protected void shouldSkipTest(String testName) throws IOException {
if (shouldSkipTestsWithSemanticTextFields()) {
assumeFalse("semantic_text tests are muted", testCase.requiredCapabilities.contains(SEMANTIC_TEXT_TYPE.capabilityName()));
}
if (supportsSourceFieldMapping() == false) {
assumeFalse("source mapping tests are muted", testCase.requiredCapabilities.contains(SOURCE_FIELD_MAPPING.capabilityName()));
}
}

protected static void checkCapabilities(RestClient client, TestFeatureService testFeatureService, String testName, CsvTestCase testCase)
Expand Down Expand Up @@ -240,6 +247,10 @@ protected boolean supportsIndexModeLookup() throws IOException {
return true;
}

protected boolean supportsSourceFieldMapping() throws IOException {
return true;
}

protected final void doTest() throws Throwable {
RequestObjectBuilder builder = new RequestObjectBuilder(randomFrom(XContentType.values()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public abstract class GenerativeRestTest extends ESRestTestCase {
@Before
public void setup() throws IOException {
if (indexExists(CSV_DATASET_MAP.keySet().iterator().next()) == false) {
loadDataSetIntoEs(client(), true);
loadDataSetIntoEs(client(), true, true);
}
}

Expand Down
Loading