diff --git a/docs/changelog/111834.yaml b/docs/changelog/111834.yaml deleted file mode 100644 index 4548dee5f91e5..0000000000000 --- a/docs/changelog/111834.yaml +++ /dev/null @@ -1,5 +0,0 @@ -pr: 111834 -summary: Add inner hits support to semantic query -area: Search -type: enhancement -issues: [] diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapper.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapper.java index e0ad044f597ab..0483296cd2c6a 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapper.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapper.java @@ -40,7 +40,6 @@ import org.elasticsearch.index.mapper.ValueFetcher; import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper; import org.elasticsearch.index.mapper.vectors.SparseVectorFieldMapper; -import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.MatchNoneQueryBuilder; import org.elasticsearch.index.query.NestedQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; @@ -55,7 +54,6 @@ import org.elasticsearch.xcontent.XContentParserConfiguration; import org.elasticsearch.xpack.core.ml.inference.results.MlTextEmbeddingResults; import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults; -import org.elasticsearch.xpack.inference.queries.SemanticQueryInnerHitBuilder; import java.io.IOException; import java.util.ArrayList; @@ -470,12 +468,7 @@ public boolean fieldHasValue(FieldInfos fieldInfos) { return fieldInfos.fieldInfo(getEmbeddingsFieldName(name())) != null; } - public QueryBuilder semanticQuery( - InferenceResults inferenceResults, - float boost, - String queryName, - SemanticQueryInnerHitBuilder semanticInnerHitBuilder - ) { + public QueryBuilder semanticQuery(InferenceResults inferenceResults, float boost, String queryName) { String nestedFieldPath = getChunksFieldName(name()); String inferenceResultsFieldName = getEmbeddingsFieldName(name()); QueryBuilder childQueryBuilder; @@ -531,10 +524,7 @@ public QueryBuilder semanticQuery( }; } - InnerHitBuilder innerHitBuilder = semanticInnerHitBuilder != null ? semanticInnerHitBuilder.toInnerHitBuilder() : null; - return new NestedQueryBuilder(nestedFieldPath, childQueryBuilder, ScoreMode.Max).boost(boost) - .queryName(queryName) - .innerHit(innerHitBuilder); + return new NestedQueryBuilder(nestedFieldPath, childQueryBuilder, ScoreMode.Max).boost(boost).queryName(queryName); } private String generateQueryInferenceResultsTypeMismatchMessage(InferenceResults inferenceResults, String expectedResultsType) { diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilder.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilder.java index 901de30145f7d..00d44b8e7a0e2 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilder.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilder.java @@ -16,7 +16,6 @@ import org.elasticsearch.cluster.metadata.InferenceFieldMetadata; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.core.Nullable; import org.elasticsearch.features.NodeFeature; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.AbstractQueryBuilder; @@ -46,9 +45,7 @@ import java.util.Map; import java.util.Objects; -import static org.elasticsearch.TransportVersions.SEMANTIC_QUERY_INNER_HITS; import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg; -import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.elasticsearch.xpack.core.ClientHelper.ML_ORIGIN; import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; @@ -59,33 +56,26 @@ public class SemanticQueryBuilder extends AbstractQueryBuilder PARSER = new ConstructingObjectParser<>( NAME, false, - args -> new SemanticQueryBuilder((String) args[0], (String) args[1], (SemanticQueryInnerHitBuilder) args[2]) + args -> new SemanticQueryBuilder((String) args[0], (String) args[1]) ); static { PARSER.declareString(constructorArg(), FIELD_FIELD); PARSER.declareString(constructorArg(), QUERY_FIELD); - PARSER.declareObject(optionalConstructorArg(), (p, c) -> SemanticQueryInnerHitBuilder.fromXContent(p), INNER_HITS_FIELD); declareStandardFields(PARSER); } private final String fieldName; private final String query; - private final SemanticQueryInnerHitBuilder innerHitBuilder; private final SetOnce inferenceResultsSupplier; private final InferenceResults inferenceResults; private final boolean noInferenceResults; public SemanticQueryBuilder(String fieldName, String query) { - this(fieldName, query, null); - } - - public SemanticQueryBuilder(String fieldName, String query, @Nullable SemanticQueryInnerHitBuilder innerHitBuilder) { if (fieldName == null) { throw new IllegalArgumentException("[" + NAME + "] requires a " + FIELD_FIELD.getPreferredName() + " value"); } @@ -94,25 +84,15 @@ public SemanticQueryBuilder(String fieldName, String query, @Nullable SemanticQu } this.fieldName = fieldName; this.query = query; - this.innerHitBuilder = innerHitBuilder; this.inferenceResults = null; this.inferenceResultsSupplier = null; this.noInferenceResults = false; - - if (this.innerHitBuilder != null) { - this.innerHitBuilder.setFieldName(fieldName); - } } public SemanticQueryBuilder(StreamInput in) throws IOException { super(in); this.fieldName = in.readString(); this.query = in.readString(); - if (in.getTransportVersion().onOrAfter(SEMANTIC_QUERY_INNER_HITS)) { - this.innerHitBuilder = in.readOptionalWriteable(SemanticQueryInnerHitBuilder::new); - } else { - this.innerHitBuilder = null; - } this.inferenceResults = in.readOptionalNamedWriteable(InferenceResults.class); this.noInferenceResults = in.readBoolean(); this.inferenceResultsSupplier = null; @@ -125,21 +105,6 @@ protected void doWriteTo(StreamOutput out) throws IOException { } out.writeString(fieldName); out.writeString(query); - if (out.getTransportVersion().onOrAfter(SEMANTIC_QUERY_INNER_HITS)) { - out.writeOptionalWriteable(innerHitBuilder); - } else if (innerHitBuilder != null) { - throw new IllegalStateException( - "Transport version must be at least [" - + SEMANTIC_QUERY_INNER_HITS.toReleaseVersion() - + "] to use [ " - + INNER_HITS_FIELD.getPreferredName() - + "] in [" - + NAME - + "], current transport version is [" - + out.getTransportVersion().toReleaseVersion() - + "]. Are you running a mixed-version cluster?" - ); - } out.writeOptionalNamedWriteable(inferenceResults); out.writeBoolean(noInferenceResults); } @@ -152,7 +117,6 @@ private SemanticQueryBuilder( ) { this.fieldName = other.fieldName; this.query = other.query; - this.innerHitBuilder = other.innerHitBuilder; this.boost = other.boost; this.queryName = other.queryName; this.inferenceResultsSupplier = inferenceResultsSupplier; @@ -160,10 +124,6 @@ private SemanticQueryBuilder( this.noInferenceResults = noInferenceResults; } - public SemanticQueryInnerHitBuilder innerHit() { - return innerHitBuilder; - } - @Override public String getWriteableName() { return NAME; @@ -183,9 +143,6 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.startObject(NAME); builder.field(FIELD_FIELD.getPreferredName(), fieldName); builder.field(QUERY_FIELD.getPreferredName(), query); - if (innerHitBuilder != null) { - builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitBuilder); - } boostAndQueryNameToXContent(builder); builder.endObject(); } @@ -212,7 +169,7 @@ private QueryBuilder doRewriteBuildSemanticQuery(SearchExecutionContext searchEx ); } - return semanticTextFieldType.semanticQuery(inferenceResults, boost(), queryName(), innerHitBuilder); + return semanticTextFieldType.semanticQuery(inferenceResults, boost(), queryName()); } else { throw new IllegalArgumentException( "Field [" + fieldName + "] of type [" + fieldType.typeName() + "] does not support " + NAME + " queries" @@ -347,12 +304,11 @@ private static String getInferenceIdForForField(Collection indexM protected boolean doEquals(SemanticQueryBuilder other) { return Objects.equals(fieldName, other.fieldName) && Objects.equals(query, other.query) - && Objects.equals(innerHitBuilder, other.innerHitBuilder) && Objects.equals(inferenceResults, other.inferenceResults); } @Override protected int doHashCode() { - return Objects.hash(fieldName, query, innerHitBuilder, inferenceResults); + return Objects.hash(fieldName, query, inferenceResults); } } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryInnerHitBuilder.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryInnerHitBuilder.java deleted file mode 100644 index 776ce990665ac..0000000000000 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryInnerHitBuilder.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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.inference.queries; - -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.index.query.InnerHitBuilder; -import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.search.fetch.subphase.FetchSourceContext; -import org.elasticsearch.xcontent.ObjectParser; -import org.elasticsearch.xcontent.ToXContentObject; -import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xcontent.XContentParser; -import org.elasticsearch.xpack.inference.mapper.SemanticTextField; - -import java.io.IOException; -import java.util.Objects; - -import static org.elasticsearch.index.query.InnerHitBuilder.DEFAULT_FROM; -import static org.elasticsearch.index.query.InnerHitBuilder.DEFAULT_SIZE; - -public class SemanticQueryInnerHitBuilder implements Writeable, ToXContentObject { - private static final ObjectParser PARSER = new ObjectParser<>( - "semantic_query_inner_hits", - SemanticQueryInnerHitBuilder::new - ); - - static { - PARSER.declareInt(SemanticQueryInnerHitBuilder::setFrom, SearchSourceBuilder.FROM_FIELD); - PARSER.declareInt(SemanticQueryInnerHitBuilder::setSize, SearchSourceBuilder.SIZE_FIELD); - } - - private String fieldName; - private int from = DEFAULT_FROM; - private int size = DEFAULT_SIZE; - - public SemanticQueryInnerHitBuilder() { - this.fieldName = null; - } - - public SemanticQueryInnerHitBuilder(StreamInput in) throws IOException { - fieldName = in.readOptionalString(); - from = in.readVInt(); - size = in.readVInt(); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeOptionalString(fieldName); - out.writeVInt(from); - out.writeVInt(size); - } - - public String getFieldName() { - return fieldName; - } - - public void setFieldName(String fieldName) { - this.fieldName = fieldName; - } - - public int getFrom() { - return from; - } - - public SemanticQueryInnerHitBuilder setFrom(int from) { - this.from = from; - return this; - } - - public int getSize() { - return size; - } - - public SemanticQueryInnerHitBuilder setSize(int size) { - this.size = size; - return this; - } - - public InnerHitBuilder toInnerHitBuilder() { - if (fieldName == null) { - throw new IllegalStateException("fieldName must have a value"); - } - - return new InnerHitBuilder(fieldName).setFrom(from) - .setSize(size) - .setFetchSourceContext(FetchSourceContext.of(true, null, new String[] { SemanticTextField.getEmbeddingsFieldName(fieldName) })); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - // Don't include name in XContent because it is hard-coded - builder.startObject(); - if (from != DEFAULT_FROM) { - builder.field(SearchSourceBuilder.FROM_FIELD.getPreferredName(), from); - } - if (size != DEFAULT_SIZE) { - builder.field(SearchSourceBuilder.SIZE_FIELD.getPreferredName(), size); - } - builder.endObject(); - return builder; - } - - public static SemanticQueryInnerHitBuilder fromXContent(XContentParser parser) throws IOException { - return PARSER.parse(parser, new SemanticQueryInnerHitBuilder(), null); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SemanticQueryInnerHitBuilder that = (SemanticQueryInnerHitBuilder) o; - return from == that.from && size == that.size && Objects.equals(fieldName, that.fieldName); - } - - @Override - public int hashCode() { - return Objects.hash(fieldName, from, size); - } - - @Override - public String toString() { - return Strings.toString(this, true, true); - } -} diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilderTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilderTests.java index 47ac33a5cf9ab..f54ce89183079 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilderTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/queries/SemanticQueryBuilderTests.java @@ -31,9 +31,7 @@ import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper; -import org.elasticsearch.index.query.InnerHitContextBuilder; import org.elasticsearch.index.query.MatchNoneQueryBuilder; -import org.elasticsearch.index.query.NestedQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.SearchExecutionContext; @@ -64,9 +62,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import static org.apache.lucene.search.BooleanClause.Occur.FILTER; import static org.apache.lucene.search.BooleanClause.Occur.MUST; @@ -169,14 +165,7 @@ protected SemanticQueryBuilder doCreateTestQueryBuilder() { queryTokens.add(randomAlphaOfLength(QUERY_TOKEN_LENGTH)); } - SemanticQueryInnerHitBuilder innerHitBuilder = null; - if (randomBoolean()) { - innerHitBuilder = new SemanticQueryInnerHitBuilder(); - innerHitBuilder.setFrom(randomIntBetween(0, 100)); - innerHitBuilder.setSize(randomIntBetween(0, 100)); - } - - SemanticQueryBuilder builder = new SemanticQueryBuilder(SEMANTIC_TEXT_FIELD, String.join(" ", queryTokens), innerHitBuilder); + SemanticQueryBuilder builder = new SemanticQueryBuilder(SEMANTIC_TEXT_FIELD, String.join(" ", queryTokens)); if (randomBoolean()) { builder.boost((float) randomDoubleBetween(0.1, 10.0, true)); } @@ -201,21 +190,6 @@ protected void doAssertLuceneQuery(SemanticQueryBuilder queryBuilder, Query quer case SPARSE_EMBEDDING -> assertSparseEmbeddingLuceneQuery(nestedQuery.getChildQuery()); case TEXT_EMBEDDING -> assertTextEmbeddingLuceneQuery(nestedQuery.getChildQuery()); } - - if (queryBuilder.innerHit() != null) { - // Rewrite to a nested query - QueryBuilder rewrittenQueryBuilder = rewriteQuery(queryBuilder, createQueryRewriteContext(), createSearchExecutionContext()); - assertThat(rewrittenQueryBuilder, instanceOf(NestedQueryBuilder.class)); - - NestedQueryBuilder nestedQueryBuilder = (NestedQueryBuilder) rewrittenQueryBuilder; - Map innerHitInternals = new HashMap<>(); - InnerHitContextBuilder.extractInnerHits(nestedQueryBuilder, innerHitInternals); - assertThat(innerHitInternals.size(), equalTo(1)); - - InnerHitContextBuilder innerHits = innerHitInternals.get(queryBuilder.innerHit().getFieldName()); - assertNotNull(innerHits); - assertThat(innerHits.innerHitBuilder(), equalTo(queryBuilder.innerHit().toInnerHitBuilder())); - } } private void assertSparseEmbeddingLuceneQuery(Query query) { @@ -338,20 +312,6 @@ public void testToXContent() throws IOException { "query": "bar" } }""", queryBuilder); - - SemanticQueryInnerHitBuilder innerHitBuilder = new SemanticQueryInnerHitBuilder().setFrom(1).setSize(2); - queryBuilder = new SemanticQueryBuilder("foo", "bar", innerHitBuilder); - checkGeneratedJson(""" - { - "semantic": { - "field": "foo", - "query": "bar", - "inner_hits": { - "from": 1, - "size": 2 - } - } - }""", queryBuilder); } public void testSerializingQueryWhenNoInferenceId() throws IOException { diff --git a/x-pack/plugin/inference/src/yamlRestTest/resources/rest-api-spec/test/inference/40_semantic_text_query.yml b/x-pack/plugin/inference/src/yamlRestTest/resources/rest-api-spec/test/inference/40_semantic_text_query.yml index 4d90d8faeb3f3..2070b3752791a 100644 --- a/x-pack/plugin/inference/src/yamlRestTest/resources/rest-api-spec/test/inference/40_semantic_text_query.yml +++ b/x-pack/plugin/inference/src/yamlRestTest/resources/rest-api-spec/test/inference/40_semantic_text_query.yml @@ -122,147 +122,6 @@ setup: - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - length: { hits.hits.0._source.inference_field.inference.chunks: 2 } ---- -"Query using a sparse embedding model and inner hits": - - requires: - cluster_features: "semantic_text.inner_hits" - reason: semantic_text inner hits support added in 8.16.0 - - - skip: - features: [ "headers", "close_to" ] - - - do: - index: - index: test-sparse-index - id: doc_1 - body: - inference_field: ["inference test", "another inference test", "yet another inference test"] - non_inference_field: "non inference test" - refresh: true - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: {} - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.1._source.text: "yet another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.1._source.embeddings - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.2._source.text: "inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.2._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "size": 1 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 1 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": 1 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 2 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "yet another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.1._source.text: "inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.1._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": 1, - "size": 1 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 1 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "yet another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": 3 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 0 } # Hits total drops to zero when you page off the end - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 0 } - --- "Numeric query using a sparse embedding model": - skip: @@ -391,147 +250,6 @@ setup: - close_to: { hits.hits.0._score: { value: 1.0, error: 0.0001 } } - length: { hits.hits.0._source.inference_field.inference.chunks: 2 } ---- -"Query using a dense embedding model and inner hits": - - requires: - cluster_features: "semantic_text.inner_hits" - reason: semantic_text inner hits support added in 8.16.0 - - - skip: - features: [ "headers", "close_to" ] - - - do: - index: - index: test-dense-index - id: doc_1 - body: - inference_field: ["inference test", "another inference test", "yet another inference test"] - non_inference_field: "non inference test" - refresh: true - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-dense-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: {} - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 1.0, error: 0.0001 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.1._source.text: "yet another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.1._source.embeddings - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.2._source.text: "another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.2._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-dense-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "size": 1 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 1.0, error: 0.0001 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 1 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-dense-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": 1 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 1.0, error: 0.0001 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 2 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "yet another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.1._source.text: "another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.1._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-dense-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": 1, - "size": 1 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 1.0, error: 0.0001 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 1 } - - match: { hits.hits.0.inner_hits.inference_field.hits.hits.0._source.text: "yet another inference test" } - - not_exists: hits.hits.0.inner_hits.inference_field.hits.hits.0._source.embeddings - - - do: - headers: - # Force JSON content type so that we use a parser that interprets the floating-point score as a double - Content-Type: application/json - search: - index: test-dense-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": 3 - } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - close_to: { hits.hits.0._score: { value: 1.0, error: 0.0001 } } - - length: { hits.hits.0._source.inference_field.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field.hits.total.value: 0 } # Hits total drops to zero when you page off the end - - length: { hits.hits.0.inner_hits.inference_field.hits.hits: 0 } - --- "Numeric query using a dense embedding model": - skip: @@ -760,101 +478,6 @@ setup: - close_to: { hits.hits.0._score: { value: 3.7837332e17, error: 1e10 } } - length: { hits.hits.0._source.inference_field.inference.chunks: 2 } ---- -"Query multiple semantic text fields with inner hits": - - requires: - cluster_features: "semantic_text.inner_hits" - reason: semantic_text inner hits support added in 8.16.0 - - - do: - indices.create: - index: test-multi-semantic-text-field-index - body: - mappings: - properties: - inference_field_1: - type: semantic_text - inference_id: sparse-inference-id - inference_field_2: - type: semantic_text - inference_id: sparse-inference-id - - - do: - index: - index: test-multi-semantic-text-field-index - id: doc_1 - body: - inference_field_1: [ "inference test 1", "another inference test 1" ] - inference_field_2: [ "inference test 2", "another inference test 2", "yet another inference test 2" ] - refresh: true - - - do: - search: - index: test-multi-semantic-text-field-index - body: - query: - bool: - must: - - semantic: - field: "inference_field_1" - query: "inference test" - inner_hits: { } - - semantic: - field: "inference_field_2" - query: "inference test" - inner_hits: { } - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - length: { hits.hits.0._source.inference_field_1.inference.chunks: 2 } - - length: { hits.hits.0._source.inference_field_2.inference.chunks: 3 } - - match: { hits.hits.0.inner_hits.inference_field_1.hits.total.value: 2 } - - length: { hits.hits.0.inner_hits.inference_field_1.hits.hits: 2 } - - match: { hits.hits.0.inner_hits.inference_field_2.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.inference_field_2.hits.hits: 3 } - ---- -"Query semantic text field in object with inner hits": - - requires: - cluster_features: "semantic_text.inner_hits" - reason: semantic_text inner hits support added in 8.16.0 - - - do: - indices.create: - index: test-semantic-text-in-object-index - body: - mappings: - properties: - container: - properties: - inference_field: - type: semantic_text - inference_id: sparse-inference-id - - - do: - index: - index: test-semantic-text-in-object-index - id: doc_1 - body: - container.inference_field: ["inference test", "another inference test", "yet another inference test"] - refresh: true - - - do: - search: - index: test-semantic-text-in-object-index - body: - query: - semantic: - field: "container.inference_field" - query: "inference test" - inner_hits: {} - - - match: { hits.total.value: 1 } - - match: { hits.hits.0._id: "doc_1" } - - exists: hits.hits.0.inner_hits.container\.inference_field - - match: { hits.hits.0.inner_hits.container\.inference_field.hits.total.value: 3 } - - length: { hits.hits.0.inner_hits.container\.inference_field.hits.hits: 3 } - --- "Query the wrong field type": - do: @@ -1216,41 +839,3 @@ setup: - match: { error.type: "resource_not_found_exception" } - match: { error.reason: "Inference endpoint not found [invalid-inference-id]" } - ---- -"Query using inner hits with invalid args": - - requires: - cluster_features: "semantic_text.inner_hits" - reason: semantic_text inner hits support added in 8.16.0 - - - do: - catch: bad_request - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "from": -1 - } - - - match: { error.root_cause.0.type: "illegal_argument_exception" } - - match: { error.root_cause.0.reason: "illegal from value, at least 0 or higher" } - - - do: - catch: bad_request - search: - index: test-sparse-index - body: - query: - semantic: - field: "inference_field" - query: "inference test" - inner_hits: { - "size": -1 - } - - - match: { error.root_cause.0.type: "illegal_argument_exception" } - - match: { error.root_cause.0.reason: "illegal size value, at least 0 or higher" }