Skip to content

S3Vectors fails to query vectors successfully

4

TLDR; querying an S3Vector index built using the public GIST dataset leads to inconsistent results from approximate nearest neighbous (ANN) search and distance values that are both excessive and irrational.

I have recently been experimenting with S3Vectors, using two public ANN datasets: DEEP 1b (shrunk to 1M vectors) and GIST 1M. DEEP has 96 dimensions of float32 values, while GIST has 960 dimensions of float32 values.

Boto3 was used to create one index for each dataset (with an Euclidean distance metric), and all vectors were uploaded using the put_vectors() operation, with batches of 500 vectors. Following the upload of all the vectors, I queried with topK=10 the DEEP index and obtained satisfactory recall scores. Subsequently, I repeated the process with the GIST index. On this occasion, recall scores were down to 0. Since I had already run the same process with both datasets using other vector databases such as Milvus, I discarded any mistakes of mine with data formatting.

I considered the possibility that the vector upload process may have encountered an issue, so I attempted to recreate the entire GIST index from the beginning. Unfortunately, this did not result in any enhancements. Once I looked further into it, I noticed something strange. When querying GIST, not only distances are crazy high, but they are the same for all the topK=10 returned ANN. I observed a similar phenomenon (possibly due to all vectors being reported at the same distance of '1.0056837035081073e+21') where multiple requests with the same query vector yielded different ANN results. The DEEP index consistently provides the same response, which is both logical and expected. The following code segment illustrates this point:

>>> import boto3, pandas as pd, numpy as np
>>> test_gist = pd.read_parquet("datasets/gist/test.parquet")
>>> test_deep = pd.read_parquet("datasets/deep_1M/test.parquet")
>>> query_gist = test_gist['emb'][0]
>>> query_deep = test_deep['emb'][0]
>>> s3vectors = boto3.client("s3vectors", "us-east-1", aws_access_key_id="****", aws_secret_access_key="****")
>>> s3vectors.query_vectors(vectorBucketName="****", indexName="gist-1m", queryVector={"float32": query_gist.tolist()}, topK=10, returnDistance=True, returnMetadata=False)
{'ResponseMetadata': {'RequestId': '****', 'HostId': '', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Thu, 18 Sep 2025 13:39:29 GMT', 'content-type': 'application/json', 'content-length': '513', 'connection': 'keep-alive', 'x-amz-request-id': '4b879386-3552-49f6-90e1-3eb9ebc84e01', 'access-control-allow-origin': '*', 'vary': 'origin, access-control-request-method, access-control-request-headers', 'access-control-expose-headers': '*'}, 'RetryAttempts': 0}, 'vectors': [{'key': '213586', 'distance': 1.0056837035081073e+21}, {'key': '223357', 'distance': 1.0056837035081073e+21}, {'key': '681653', 'distance': 1.0056837035081073e+21}, {'key': '455236', 'distance': 1.0056837035081073e+21}, {'key': '755307', 'distance': 1.0056837035081073e+21}, {'key': '325642', 'distance': 1.0056837035081073e+21}, {'key': '921564', 'distance': 1.0056837035081073e+21}, {'key': '134104', 'distance': 1.0056837035081073e+21}, {'key': '597373', 'distance': 1.0056837035081073e+21}, {'key': '429873', 'distance': 1.0056837035081073e+21}]}
>>> s3vectors.query_vectors(vectorBucketName="****", indexName="gist-1m", queryVector={"float32": query_gist.tolist()}, topK=10, returnDistance=True, returnMetadata=False)
{'ResponseMetadata': {'RequestId': '****', 'HostId': '', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Thu, 18 Sep 2025 13:39:35 GMT', 'content-type': 'application/json', 'content-length': '513', 'connection': 'keep-alive', 'x-amz-request-id': '718f3ced-ce97-40a1-8575-fc7b6339721c', 'access-control-allow-origin': '*', 'vary': 'origin, access-control-request-method, access-control-request-headers', 'access-control-expose-headers': '*'}, 'RetryAttempts': 0}, 'vectors': [{'key': '429873', 'distance': 1.0056837035081073e+21}, {'key': '681653', 'distance': 1.0056837035081073e+21}, {'key': '223357', 'distance': 1.0056837035081073e+21}, {'key': '455236', 'distance': 1.0056837035081073e+21}, {'key': '213586', 'distance': 1.0056837035081073e+21}, {'key': '597373', 'distance': 1.0056837035081073e+21}, {'key': '134104', 'distance': 1.0056837035081073e+21}, {'key': '921564', 'distance': 1.0056837035081073e+21}, {'key': '755307', 'distance': 1.0056837035081073e+21}, {'key': '325642', 'distance': 1.0056837035081073e+21}]}
>>> 
>>> 
>>> s3vectors.query_vectors(vectorBucketName="****", indexName="deep-1m", queryVector={"float32": query_deep.tolist()}, topK=10, returnDistance=True, returnMetadata=False)
{'ResponseMetadata': {'RequestId': '****', 'HostId': '', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Thu, 18 Sep 2025 13:40:25 GMT', 'content-type': 'application/json', 'content-length': '484', 'connection': 'keep-alive', 'x-amz-request-id': 'df3bf080-9d7a-41ce-8b51-0890f3076eed', 'access-control-allow-origin': '*', 'vary': 'origin, access-control-request-method, access-control-request-headers', 'access-control-expose-headers': '*'}, 'RetryAttempts': 0}, 'vectors': [{'key': '358460', 'distance': 0.3194265365600586}, {'key': '743231', 'distance': 0.3194948136806488}, {'key': '145424', 'distance': 0.3463098108768463}, {'key': '896261', 'distance': 0.3465639054775238}, {'key': '518009', 'distance': 0.3480227589607239}, {'key': '688179', 'distance': 0.34803152084350586}, {'key': '653623', 'distance': 0.3497021198272705}, {'key': '378861', 'distance': 0.3508843779563904}, {'key': '630358', 'distance': 0.3524482846260071}, {'key': '214441', 'distance': 0.3544580042362213}]}
>>> s3vectors.query_vectors(vectorBucketName="****", indexName="deep-1m", queryVector={"float32": query_deep.tolist()}, topK=10, returnDistance=True, returnMetadata=False)
{'ResponseMetadata': {'RequestId': '****', 'HostId': '', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Thu, 18 Sep 2025 13:40:31 GMT', 'content-type': 'application/json', 'content-length': '484', 'connection': 'keep-alive', 'x-amz-request-id': 'ec907813-6f28-4d1f-9577-8a33f873cc11', 'access-control-allow-origin': '*', 'vary': 'origin, access-control-request-method, access-control-request-headers', 'access-control-expose-headers': '*'}, 'RetryAttempts': 0}, 'vectors': [{'key': '358460', 'distance': 0.3194265365600586}, {'key': '743231', 'distance': 0.3194948136806488}, {'key': '145424', 'distance': 0.3463098108768463}, {'key': '896261', 'distance': 0.3465639054775238}, {'key': '518009', 'distance': 0.3480227589607239}, {'key': '688179', 'distance': 0.34803152084350586}, {'key': '653623', 'distance': 0.3497021198272705}, {'key': '378861', 'distance': 0.3508843779563904}, {'key': '630358', 'distance': 0.3524482846260071}, {'key': '214441', 'distance': 0.3544580042362213}]}

To ensure absolute certainty, I retrieved the so-called closest vector using the query_vectors() function, specifying the index name and the key value of the vector. I then manually calculated the Euclidean distance to the query vector using the following code: np.linalg.norm(query_vector - retrieved_vector). The distance obtained (an order of 1e+8) was significantly lower than the one indicated by S3Vectors, re-affirming my suspicions that while data is uploaded correctly, somehow the distance calculation for ANN search is not working as expected.

The one thing I can't understand is the reason while S3Vectors is failing vector search on this dataset specifically. The only differences between the two datasets would be the dimension of their vectors and the value range of the embeddings. While DEEP has 96 values ranging from -1 to 1, GIST is composed of 960 int values casted to float32 (e.g. 172839018.0). I have assumed that neither of these would pose a problem, given that:

  1. The same exact GIST dataset has been successful when used with other vector databases, both for indexing and querying.
  2. I created a synthetic 768-dimensional index based on the Deep dataset. This was achieved by tiling the original 96D embeddings eight times, in an attempt to create an alternative high-dimensional dataset. The query was successfully submitted and received consistent responses.
  3. The distance metric applied in all scenarios is the well-established Euclidean distance, which is particularly effective for non-normalized values.
  4. I have conducted a series of tests using both Boto3 (as described above) and VectorDBBench (the official Zilliztech benchmark tool for vectordb) with the AWS S3Vectors client. In all cases, the GIST dataset underperforms, with recall rates of 0.0 while other datasets perform well.

I would be very grateful if someone with specific S3Vectors knowledge could help me find the root of this intriguing problem.

asked a year ago370 views

1 Answer
-4

The issue you're experiencing with S3 Vectors when querying the GIST dataset appears to be related to the high dimensionality (960) and the large value range of the embeddings in this dataset.

S3 Vectors is designed to deliver 90%+ average recall for most datasets, but performance can vary depending on specific use cases. Several factors affect recall performance, including:

  1. The vector embedding model
  2. The size of the vector dataset (number of vectors and dimensions)
  3. The distribution of queries

The extremely high distance values (1.0056837035081073e+21) you're seeing with the GIST dataset suggest a potential numerical overflow or precision issue when calculating distances with such high-dimensional vectors containing large values. This would explain why you're getting inconsistent results and why all vectors appear to have the same distance.

Your synthetic test with the 768-dimensional index created by tiling the normalized DEEP dataset worked because those values remained within a reasonable range (-1 to 1), unlike the GIST dataset with its large integer values cast to float32.

For high-dimensional vectors like those in the GIST dataset (960 dimensions), especially with large numerical values, you might need to:

  1. Normalize the vectors before storing them in S3 Vectors
  2. Consider dimensionality reduction techniques
  3. Test with a different distance metric if available

Since you mentioned the same dataset works with other vector databases, there may be specific limitations or optimizations in S3 Vectors that affect its performance with this particular data distribution.

For your use case with the GIST dataset, you might need to preprocess the data differently or consider alternative vector storage solutions if S3 Vectors continues to perform poorly with this specific dataset.
Sources
Querying vectors - Amazon Simple Storage Service
Amazon S3 Vectors: Revolutionizing AI Data Storage with Use Cases | AWS re:Post

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.