diff --git a/docs/changelog/135994.yaml b/docs/changelog/135994.yaml new file mode 100644 index 0000000000000..ada5e579eb46d --- /dev/null +++ b/docs/changelog/135994.yaml @@ -0,0 +1,6 @@ +pr: 135994 +summary: Cardinality Aggregator Throws `UnsupportedOperationException` When Field + Type is Vector +area: Vector Search +type: bug +issues: [] diff --git a/server/src/main/java/org/elasticsearch/index/mapper/vectors/VectorDVLeafFieldData.java b/server/src/main/java/org/elasticsearch/index/mapper/vectors/VectorDVLeafFieldData.java index e44202d353629..120682d185535 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/vectors/VectorDVLeafFieldData.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/vectors/VectorDVLeafFieldData.java @@ -60,7 +60,7 @@ public long ramBytesUsed() { @Override public SortedBinaryDocValues getBytesValues() { - throw new UnsupportedOperationException("String representation of doc values for vector fields is not supported"); + throw new IllegalArgumentException("String representation of doc values for vector fields is not supported"); } @Override diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregator.java index 05aa80f06448d..6aa7010946c2f 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregator.java @@ -32,6 +32,8 @@ import org.elasticsearch.index.fielddata.NumericDoubleValues; import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; +import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper; +import org.elasticsearch.index.mapper.vectors.SparseVectorFieldMapper; import org.elasticsearch.search.aggregations.AggregationExecutionContext; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -73,6 +75,11 @@ public CardinalityAggregator( ) throws IOException { super(name, context, parent, metadata); assert valuesSourceConfig.hasValues(); + if (valuesSourceConfig.fieldContext() != null + && (valuesSourceConfig.fieldContext().fieldType() instanceof DenseVectorFieldMapper.DenseVectorFieldType + || valuesSourceConfig.fieldContext().fieldType() instanceof SparseVectorFieldMapper.SparseVectorFieldType)) { + throw new IllegalArgumentException("Cardinality aggregation [" + name + "] does not support vector fields"); + } this.valuesSource = valuesSourceConfig.getValuesSource(); this.precision = precision; this.counts = new HyperLogLogPlusPlus(precision, context.bigArrays(), 1); diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java index 8169a5301f00f..9beb676bdd699 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.core.CheckedConsumer; +import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.mapper.IpFieldMapper; import org.elasticsearch.index.mapper.KeywordFieldMapper; @@ -38,6 +39,8 @@ import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.mapper.RangeFieldMapper; import org.elasticsearch.index.mapper.RangeType; +import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper; +import org.elasticsearch.index.mapper.vectors.SparseVectorFieldMapper; import org.elasticsearch.script.MockScriptEngine; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptEngine; @@ -217,6 +220,51 @@ public void testQueryFiltersAll() throws IOException { }); } + public void testVectorValueThrows() { + final CardinalityAggregationBuilder aggregationBuilder = new CardinalityAggregationBuilder("card_agg_name").field("vector_value"); + final MappedFieldType mappedFieldTypes; + boolean isDense = randomBoolean(); + if (isDense) { + mappedFieldTypes = new DenseVectorFieldMapper.DenseVectorFieldType( + "vector_value", + IndexVersion.current(), + DenseVectorFieldMapper.ElementType.FLOAT, + 64, + true, + DenseVectorFieldMapper.VectorSimilarity.COSINE, + DenseVectorFieldMapper.VectorIndexType.FLAT.parseIndexOptions("vector_value", new HashMap<>(), IndexVersion.current()), + new HashMap<>(), + false + ); + } else { + mappedFieldTypes = new SparseVectorFieldMapper.SparseVectorFieldType( + IndexVersion.current(), + "vector_value", + false, + new HashMap<>() + ); + } + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> testAggregation(aggregationBuilder, new MatchAllDocsQuery(), iw -> { + iw.addDocument(singleton(new SortedDocValuesField("vector_value", new BytesRef("one")))); + iw.addDocument(singleton(new SortedDocValuesField("unrelatedField", new BytesRef("two")))); + iw.addDocument(singleton(new SortedDocValuesField("str_value", new BytesRef("three")))); + iw.addDocument(singleton(new SortedDocValuesField("str_value", new BytesRef("one")))); + }, card -> { + assertEquals(2, card.getValue(), 0); + assertTrue(AggregationInspectionHelper.hasValue(card)); + }, mappedFieldTypes) + ); + + if (isDense) { + assertEquals("Cardinality aggregation [card_agg_name] does not support vector fields", exception.getMessage()); + } else { + assertEquals("[sparse_vector] fields do not support sorting, scripting or aggregating", exception.getMessage()); + } + } + public void testSingleValuedString() throws IOException { final CardinalityAggregationBuilder aggregationBuilder = new CardinalityAggregationBuilder("name").field("str_value"); final MappedFieldType mappedFieldTypes = new KeywordFieldMapper.KeywordFieldType("str_value");