Skip to content

Add support for sparse_vector queries against semantic_text fields #118617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

kderusso
Copy link
Member

@kderusso kderusso commented Dec 12, 2024

Adds support for running sparse_vector queries directly against semantic_text fields without having to pass in nested queries from the client or knowing the semantic subfields.

Example script to test:

PUT _inference/sparse_embedding/my-elser-endpoint
{
  "service": "elser",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1
  },
  "task_settings": {}
}

PUT my-index-1
{
    "mappings": {
        "properties": {
            "inference_field": {
                "type": "semantic_text",
                "inference_id": "my-elser-endpoint"
            },
            "text_field": {
                "type": "text"
            }
        }
    }
}

PUT my-index-2
{
  "mappings": {
    "properties": {
      "inference_field": {
        "type": "sparse_vector"
      },
      "text_field": { 
        "type": "text"
      }
    }
  }
}

POST my-index-1/_doc/1
{
  "inference_field": "a test value",
  "text_field": "a test value"
}

POST my-index-2/_doc/1
{
  "inference_field": { "test": 1, "value": 2, "foo": 3 }, 
  "text_field": "another test value"
}

// Vanilla nested sparse vector query with inference still works
GET my-index-1/_search
{
  "query": {
    "nested": {
      "path": "inference_field.inference.chunks",
      "query": {
        "sparse_vector": {
          "field": "inference_field.inference.chunks.embeddings",
          "inference_id": "my-elser-endpoint",
          "query": "test"
        }
      }
    }
  }
}

// Vanilla nested sparse vector query with query vectors still works
GET my-index-1/_search
{
  "query": {
    "nested": {
      "path": "inference_field.inference.chunks",
      "query": {
        "sparse_vector": {
          "field": "inference_field.inference.chunks.embeddings",
          "query_vector": { "test": 2.9, "value": 2.7 }
        }
      }
    }
  }
}

// Sparse vector against a semantic text field using inference
GET my-index-1/_search
{
  "query": {
    "sparse_vector": {
      "field": "inference_field",
      "inference_id": "my-elser-endpoint",
      "query": "test"
    }
  }
}

// Sparse vector against a semantic text field using query vectors
GET my-index-1/_search
{
  "query": {
    "sparse_vector": {
      "field": "inference_field",
      "query_vector": {
        "test": 2.9,
        "value": 2.7
      }
    }
  }
}

// Combined search using inference
GET my-index-*/_search
{
  "query": {
    "sparse_vector": {
      "field": "inference_field",
      "inference_id": "my-elser-endpoint",
      "query": "test"
    }
  }
}

// Combined search using query vectors 
GET my-index-*/_search
{
  "query": {
    "sparse_vector": {
      "field": "inference_field",
      "query_vector": {
        "test": 2.9,
        "value": 2.7
      }
    }
  }
}

@kderusso kderusso changed the title Kderusso/sparse vector semantic text field Add support for sparse_vector queries against semantic_text fields Dec 12, 2024
@kderusso kderusso added >enhancement auto-backport Automatically create backport pull requests when merged v8.18.0 :Search Relevance/Search Catch all for Search Relevance labels Dec 12, 2024
@elasticsearchmachine
Copy link
Collaborator

Hi @kderusso, I've created a changelog YAML for you.

@kderusso kderusso force-pushed the kderusso/sparse-vector-semantic-text-field branch from 56d3f46 to c427143 Compare December 12, 2024 20:38
@kderusso kderusso added the :SearchOrg/Relevance Label for the Search (solution/org) Relevance team label Dec 12, 2024
@kderusso kderusso marked this pull request as ready for review December 12, 2024 20:39
@kderusso kderusso requested a review from a team December 12, 2024 20:40
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-search-relevance (Team:Search Relevance)

@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/search-eng (Team:SearchOrg)

@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/search-relevance (Team:Search - Relevance)

@elasticsearchmachine
Copy link
Collaborator

Hi @kderusso, I've created a changelog YAML for you.

Copy link
Member

@carlosdelest carlosdelest left a comment

Choose a reason for hiding this comment

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

Looks great! Love how the rewriting mechanism makes this soooo easy!

++ to Ben's comment on removing the need for inference_id for semantic_text.

A refactoring question about introducing a base class for your consideration.

new SparseVectorQueryBuilder(
getNestedEmbeddingsField(sparseVectorQueryBuilder.getFieldName()),
sparseVectorQueryBuilder.getQueryVectors(),
sparseVectorQueryBuilder.getInferenceId(),
Copy link
Member

Choose a reason for hiding this comment

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

This should be retrieved from the index metadata, as per Ben's comment this is not necessary

Copy link
Member Author

Choose a reason for hiding this comment

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

For some use cases we will still need it, for example if the request is sending in query vectors. However, I will address this

Copy link
Contributor

@Mikep86 Mikep86 left a comment

Choose a reason for hiding this comment

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

Great work, I think the overall mechanism for implementing this looks good. I agree with Ben and Carlos that we need to work on making inference_id optional when querying a semantic_text field in this PR, as it is a big part of the user experience.

Comment on lines 46 to 54
boolQueryBuilder.should(
SemanticQueryInterceptionUtils.createSubQueryForIndices(
inferenceIndexInformationForField.nonInferenceIndices(),
SemanticQueryInterceptionUtils.createSubQueryForIndices(
inferenceIndexInformationForField.nonInferenceIndices(),
sparseVectorQueryBuilder
)
)
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why nest two sub-queries like this?

@kderusso kderusso marked this pull request as draft December 13, 2024 14:14
@kderusso
Copy link
Member Author

Discussed this offline with the team, and we decided that the use case of not specifying inference ID should happen in this PR. So, I am moving it back to Draft status until it's ready for re-review.

@kderusso kderusso marked this pull request as ready for review December 16, 2024 20:41
Copy link
Member

@carlosdelest carlosdelest left a comment

Choose a reason for hiding this comment

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

LGTM 👍

// Edge case, where inference_id was not specified in the request,
// but we did not intercept this and rewrite to a query o field with
// pre-configured inference. So we trap here and output a nicer error message.
throw new IllegalArgumentException("inference_id required to perform vector search on query string");
Copy link
Member

Choose a reason for hiding this comment

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

Including the field name and the (wrong) field type might help users diagnose the problem

Suggested change
throw new IllegalArgumentException("inference_id required to perform vector search on query string");
throw new IllegalArgumentException("inference_id required to perform a sparse_vector query on sparse_vector field [" + fieldName + "]");

Copy link
Member

Choose a reason for hiding this comment

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

Nice refactoring! 👏

Copy link
Member

@benwtrent benwtrent left a comment

Choose a reason for hiding this comment

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

I really like the refactoring. I think the change overall is good.

I would REALLY like a QueryInterceptorTestCase and then two sets of tests

SemanticMatchQueryRewriteInterceptorTests and SemanticQueryRewriteInterceptorTests

I know all the yaml tests SHOULD be covering every branch, but I think having purpose driven interceptor tests that assert rewritten query types and shapes would be really nice.

Comment on lines +155 to +165
- do:
search:
index: test-semantic-text-index
body:
query:
sparse_vector:
field: inference_field
query: "inference test"

- match: { hits.total.value: 1 }
- match: { hits.hits.0._id: "doc_1" }
Copy link
Member

Choose a reason for hiding this comment

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

❤️

@kderusso
Copy link
Member Author

I would REALLY like a QueryInterceptorTestCase and then two sets of tests

SemanticMatchQueryRewriteInterceptorTests and SemanticQueryRewriteInterceptorTests

@benwtrent As a compromise, I'm adding some Java tests here with the expectation that they'll be cleaned up and fleshed out more in a followup PR.

@kderusso kderusso merged commit 15bec3c into elastic:main Dec 18, 2024
16 checks passed
@elasticsearchmachine
Copy link
Collaborator

💔 Backport failed

Status Branch Result
8.x Commit could not be cherrypicked due to conflicts

You can use sqren/backport to manually backport by running backport --upstream elastic/elasticsearch --pr 118617

@kderusso
Copy link
Member Author

💚 All backports created successfully

Status Branch Result
8.x

Questions ?

Please refer to the Backport tool documentation

kderusso added a commit to kderusso/elasticsearch that referenced this pull request Dec 18, 2024
…lastic#118617)

(cherry picked from commit 15bec3c)

# Conflicts:
#	x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferenceFeatures.java
rjernst pushed a commit to rjernst/elasticsearch that referenced this pull request Dec 18, 2024
kderusso added a commit that referenced this pull request Dec 18, 2024
…118617) (#118951)

(cherry picked from commit 15bec3c)

# Conflicts:
#	x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferenceFeatures.java
navarone-feekery pushed a commit to navarone-feekery/elasticsearch that referenced this pull request Dec 26, 2024
@kderusso kderusso deleted the kderusso/sparse-vector-semantic-text-field branch January 24, 2025 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-backport Automatically create backport pull requests when merged backport pending >enhancement :Search Relevance/Search Catch all for Search Relevance :SearchOrg/Relevance Label for the Search (solution/org) Relevance team v8.18.0 v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants