From 6d274a1211e9e053412a8b7958bcfdb7febb804d Mon Sep 17 00:00:00 2001 From: John Wagster Date: Sat, 4 Oct 2025 21:51:06 -0500 Subject: [PATCH 1/3] throw illegal arg exception when vectors are used for card aggs --- .../mapper/vectors/VectorDVLeafFieldData.java | 2 +- .../metrics/CardinalityAggregator.java | 6 +++ .../metrics/CardinalityAggregatorTests.java | 48 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) 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..633a6e6186c70 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,10 @@ public CardinalityAggregator( ) throws IOException { super(name, context, parent, metadata); assert valuesSourceConfig.hasValues(); + if (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 83f96742714e1..999e4e280bb51 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 @@ -32,6 +32,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; @@ -39,6 +40,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; @@ -224,6 +227,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"); From 4b28fe02e3d8c63e1e000222139a0a965ee0513b Mon Sep 17 00:00:00 2001 From: John Wagster Date: Sat, 4 Oct 2025 21:59:06 -0500 Subject: [PATCH 2/3] Update docs/changelog/135994.yaml --- docs/changelog/135994.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/changelog/135994.yaml 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: [] From 01bbef832e31ba4cdf60e38ad2d6d81682158fa6 Mon Sep 17 00:00:00 2001 From: John Wagster Date: Sat, 4 Oct 2025 23:44:59 -0500 Subject: [PATCH 3/3] add null check --- .../search/aggregations/metrics/CardinalityAggregator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 633a6e6186c70..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 @@ -75,8 +75,9 @@ public CardinalityAggregator( ) throws IOException { super(name, context, parent, metadata); assert valuesSourceConfig.hasValues(); - if (valuesSourceConfig.fieldContext().fieldType() instanceof DenseVectorFieldMapper.DenseVectorFieldType - || valuesSourceConfig.fieldContext().fieldType() instanceof SparseVectorFieldMapper.SparseVectorFieldType) { + 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();