Skip to content

Commit

Permalink
Allow dimension fields to have multiple values in standard and logsdb…
Browse files Browse the repository at this point in the history
… index mode

Fixes elastic#112232
Fixes elastic#112239
  • Loading branch information
felixbarny committed Aug 29, 2024
1 parent a97b0e2 commit cd09cb7
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 63 deletions.
4 changes: 2 additions & 2 deletions server/src/main/java/org/elasticsearch/index/IndexMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public IdFieldMapper buildIdFieldMapper(BooleanSupplier fieldDataEnabled) {

@Override
public DocumentDimensions buildDocumentDimensions(IndexSettings settings) {
return new DocumentDimensions.OnlySingleValueAllowed();
return DocumentDimensions.Noop.INSTANCE;
}

@Override
Expand Down Expand Up @@ -279,7 +279,7 @@ public MetadataFieldMapper timeSeriesRoutingHashFieldMapper() {

@Override
public DocumentDimensions buildDocumentDimensions(IndexSettings settings) {
return new DocumentDimensions.OnlySingleValueAllowed();
return DocumentDimensions.Noop.INSTANCE;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,47 @@ private void add(String fieldName) {
}
}
};

/**
* Noop implementation that doesn't perform validations on dimension fields
*/
enum Noop implements DocumentDimensions {

INSTANCE;

@Override
public DocumentDimensions addString(String fieldName, BytesRef utf8Value) {
return this;
}

@Override
public DocumentDimensions addString(String fieldName, String value) {
return this;
}

@Override
public DocumentDimensions addIp(String fieldName, InetAddress value) {
return this;
}

@Override
public DocumentDimensions addLong(String fieldName, long value) {
return this;
}

@Override
public DocumentDimensions addUnsignedLong(String fieldName, long value) {
return this;
}

@Override
public DocumentDimensions addBoolean(String fieldName, boolean value) {
return this;
}

@Override
public DocumentDimensions validate(IndexSettings settings) {
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.script.BooleanFieldScript;
Expand All @@ -25,6 +26,7 @@
import org.elasticsearch.xcontent.XContentFactory;

import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.function.Function;

Expand Down Expand Up @@ -257,15 +259,24 @@ public void testDimensionIndexedAndDocvalues() {
}
}

public void testDimensionMultiValuedField() throws IOException {
public void testDimensionMultiValuedField() throws Throwable {
XContentBuilder mapping = fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_dimension", true);
});
DocumentMapper mapper = randomBoolean() ? createDocumentMapper(mapping) : createTimeSeriesModeDocumentMapper(mapping);
IndexMode indexMode = randomFrom(IndexMode.values());
DocumentMapper mapper = createDocumentMapper(mapping, indexMode);

Exception e = expectThrows(DocumentParsingException.class, () -> mapper.parse(source(b -> b.array("field", true, false))));
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
ThrowingRunnable parseArray = () -> mapper.parse(source(b -> {
b.array("field", true, false);
b.field("@timestamp", Instant.now());
}));
if (indexMode == IndexMode.TIME_SERIES) {
Exception e = expectThrows(DocumentParsingException.class, parseArray);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
} else {
parseArray.run();
}
}

public void testDimensionInRoutingPath() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.script.IpFieldScript;
Expand All @@ -26,6 +27,7 @@

import java.io.IOException;
import java.net.InetAddress;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
Expand Down Expand Up @@ -255,17 +257,25 @@ public void testDimensionIndexedAndDocvalues() {
}
}

public void testDimensionMultiValuedField() throws IOException {
DocumentMapper mapper = createDocumentMapper(fieldMapping(b -> {
public void testDimensionMultiValuedField() throws Throwable {
XContentBuilder mapping = fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_dimension", true);
}));
});

Exception e = expectThrows(
DocumentParsingException.class,
() -> mapper.parse(source(b -> b.array("field", "192.168.1.1", "192.168.1.1")))
);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
IndexMode indexMode = randomFrom(IndexMode.values());
DocumentMapper mapper = createDocumentMapper(mapping, indexMode);

ThrowingRunnable parseArray = () -> mapper.parse(source(b -> {
b.array("field", "192.168.1.1", "192.168.1.1");
b.field("@timestamp", Instant.now());
}));
if (indexMode == IndexMode.TIME_SERIES) {
Exception e = expectThrows(DocumentParsingException.class, parseArray);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
} else {
parseArray.run();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.analysis.AnalyzerScope;
Expand All @@ -44,6 +45,7 @@
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -373,15 +375,24 @@ public void testDimensionIndexedAndDocvalues() {
}
}

public void testDimensionMultiValuedField() throws IOException {
public void testDimensionMultiValuedField() throws Throwable {
XContentBuilder mapping = fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_dimension", true);
});
DocumentMapper mapper = randomBoolean() ? createDocumentMapper(mapping) : createTimeSeriesModeDocumentMapper(mapping);
IndexMode indexMode = randomFrom(IndexMode.values());
DocumentMapper mapper = createDocumentMapper(mapping, indexMode);

Exception e = expectThrows(DocumentParsingException.class, () -> mapper.parse(source(b -> b.array("field", "1234", "45678"))));
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
ThrowingRunnable parseArray = () -> mapper.parse(source(b -> {
b.array("field", "1234", "45678");
b.field("@timestamp", Instant.now());
}));
if (indexMode == IndexMode.TIME_SERIES) {
Exception e = expectThrows(DocumentParsingException.class, parseArray);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
} else {
parseArray.run();
}
}

public void testDimensionExtraLongKeyword() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.mapper.DocumentMapper;
Expand All @@ -34,6 +35,7 @@
import org.junit.AssumptionViolatedException;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -189,18 +191,25 @@ public void testDimensionIndexedAndDocvalues() {
}
}

public void testDimensionMultiValuedField() throws IOException {
public void testDimensionMultiValuedField() throws Throwable {
XContentBuilder mapping = fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_dimensions", List.of("key1", "key2", "field3.key3"));
});
DocumentMapper mapper = randomBoolean() ? createDocumentMapper(mapping) : createTimeSeriesModeDocumentMapper(mapping);

Exception e = expectThrows(
DocumentParsingException.class,
() -> mapper.parse(source(b -> b.array("field.key1", "value1", "value2")))
);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field.key1] cannot be a multi-valued field"));
IndexMode indexMode = randomFrom(IndexMode.values());
DocumentMapper mapper = createDocumentMapper(mapping, indexMode);

ThrowingRunnable parseArray = () -> mapper.parse(source(b -> {
b.array("field.key1", "value1", "value2");
b.field("@timestamp", Instant.now());
}));
if (indexMode == IndexMode.TIME_SERIES) {
Exception e = expectThrows(DocumentParsingException.class, parseArray);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field.key1] cannot be a multi-valued field"));
} else {
parseArray.run();
}
}

public void testDisableIndex() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ protected static String randomIndexOptions() {
return randomFrom("docs", "freqs", "positions", "offsets");
}

protected final DocumentMapper createDocumentMapper(XContentBuilder mappings, IndexMode indexMode) throws IOException {
return switch (indexMode) {
case STANDARD -> createDocumentMapper(mappings);
case TIME_SERIES -> createTimeSeriesModeDocumentMapper(mappings);
case LOGSDB -> createLogsModeDocumentMapper(mappings);
};
}

protected final DocumentMapper createDocumentMapper(XContentBuilder mappings) throws IOException {
return createMapperService(mappings).documentMapper();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
package org.elasticsearch.index.mapper;

import org.apache.lucene.index.IndexableField;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.time.Instant;
import java.util.List;

import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -69,17 +72,25 @@ public void testDimensionIndexedAndDocvalues() {
}
}

public void testDimensionMultiValuedField() throws IOException {
DocumentMapper mapper = createDocumentMapper(fieldMapping(b -> {
public void testDimensionMultiValuedField() throws Throwable {
XContentBuilder mapping = fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_dimension", true);
}));
});

Exception e = expectThrows(
DocumentParsingException.class,
() -> mapper.parse(source(b -> b.array("field", randomNumber(), randomNumber(), randomNumber())))
);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
IndexMode indexMode = randomFrom(IndexMode.values());
DocumentMapper mapper = createDocumentMapper(mapping, indexMode);

ThrowingRunnable parseArray = () -> mapper.parse(source(b -> {
b.array("field", randomNumber(), randomNumber(), randomNumber());
b.field("@timestamp", Instant.now());
}));
if (indexMode == IndexMode.TIME_SERIES) {
Exception e = expectThrows(DocumentParsingException.class, parseArray);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
} else {
parseArray.run();
}
}

public void testMetricAndDimension() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import java.io.IOException;
import java.math.BigInteger;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -260,17 +261,25 @@ public void testDimensionIndexedAndDocvalues() {
}
}

public void testDimensionMultiValuedField() throws IOException {
DocumentMapper mapper = createDocumentMapper(fieldMapping(b -> {
public void testDimensionMultiValuedField() throws Throwable {
XContentBuilder mapping = fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_dimension", true);
}));
});

Exception e = expectThrows(
DocumentParsingException.class,
() -> mapper.parse(source(b -> b.array("field", randomNonNegativeLong(), randomNonNegativeLong(), randomNonNegativeLong())))
);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
IndexMode indexMode = randomFrom(IndexMode.values());
DocumentMapper mapper = createDocumentMapper(mapping, indexMode);

ThrowingRunnable parseArray = () -> mapper.parse(source(b -> {
b.array("field", randomNonNegativeLong(), randomNonNegativeLong(), randomNonNegativeLong());
b.field("@timestamp", Instant.now());
}));
if (indexMode == IndexMode.TIME_SERIES) {
Exception e = expectThrows(DocumentParsingException.class, parseArray);
assertThat(e.getCause().getMessage(), containsString("Dimension field [field] cannot be a multi-valued field"));
} else {
parseArray.run();
}
}

public void testMetricType() throws IOException {
Expand Down

This file was deleted.

Loading

0 comments on commit cd09cb7

Please sign in to comment.