Vector Collection
The primary object for interacting with vector storage is a Vector Collection. A Vector Collection holds information about the vectors and associated metadata (user values).
For further information on the architecture and considerations for the Beta version of the Vector Collection data structure, see VectorCollection data structure design.
A collection consists of one or more indexes, all sharing a common metadata storage. Each index represents a distinct vector space. Vectors from different indexes are stored independently, and indexes can have different configurations.
Conceptually, a vector collection resembles a key-value store. Here, the key is a user-defined unique identifier, and the value is an object containing metadata alongside several vectors — one for each index.
Users can store any value they require as metadata. This could be additional characteristics of the source data or even the source data itself.
JVM configuration
Vector collection indexing and search are computationally very intensive and can utilize SIMD instructions.
For best performance using vector collections, use Java 21 and enable Vector API from Project Panama by adding --add-modules jdk.incubator.vector
to JVM parameters.
If you use Docker, you can use the following example command:
docker run -p 5701:5701 -e HZ_LICENSEKEY=<your_license_key> \
-e "JAVA_OPTS=--add-modules jdk.incubator.vector -Dhazelcast.partition.count=16" \
hazelcast/hazelcast-enterprise:latest-snapshot-slim-jdk21
Configuration
Collection configuration can be set dynamically during vector collection creation or statically during cluster configuration. Unlike other data structures, the configuration must be set up before the collection can be used. There is no default configuration for the vector collection. If no matching configuration is found for the specified vector collection, the system raises an error.
The configuration supports wildcards. To retrieve a vector collection, the system can search for an exact match of the specified collection name in the configuration, or use a wildcard match from the existing configurations.
The following tables list all available options:
Option | Description | Required | Default |
---|---|---|---|
name |
The name of the vector collection.
Can include letters, numbers, and the symbols |
Required |
|
indexes |
Information about indexes configuration |
Required |
|
backup-count |
Number of synchronous backups. See Backup Types |
Optional |
|
async-backup-count |
Number of asynchronous backups. See Backup Types |
Optional |
|
Option | Description | Required | Default |
---|---|---|---|
name |
The name of the vector index.
Can include letters, numbers, and the symbols |
Required for single-index vector collections. Optional for multi-index collection |
|
dimension |
Vectors dimension |
Required |
|
metric |
Used to calculate the distance between two vectors. For further information on distance metrics, see the Available distance metrics table. |
Required |
|
max-degree |
Maximum number of neighbors per node. Note that the meaning of this parameter differs from that used in version 5.5. |
Optional |
|
ef-construction |
The size of the search queue to use when finding nearest neighbors. |
Optional |
|
use-deduplication |
Whether to use vector deduplication. When disabled, each added vector is treated as a distinct vector in the index, even if it is identical to an existing one. When enabled, the index consumes less space as duplicates share a vector, but the time required to add a vector increases. |
Optional |
|
Name | Description | Score definition |
---|---|---|
EUCLIDEAN |
Euclidean distance |
|
COSINE |
Cosine of the angle between the vectors |
|
DOT |
Dot product of the vectors |
|
The recommended method for computing cosine similarity is to normalize all vectors to unit length and use the DOT metric instead. |
Configuration example:
<hazelcast>
<vector-collection name="books">
<backup-count>1</backup-count>
<async-backup-count>0</async-backup-count>
<indexes>
<index name="word2vec-index">
<dimension>6</dimension>
<metric>DOT</metric>
</index>
<index name="glove-index">
<dimension>10</dimension>
<metric>DOT</metric>
<max-degree>32</max-degree>
<ef-construction>256</ef-construction>
<use-deduplication>false</use-deduplication>
</index>
</indexes>
</vector-collection>
</hazelcast>
hazelcast:
vector-collection:
books:
backup-count: 1
async-backup-count: 0
indexes:
- name: word2vec-index
dimension: 6
metric: DOT
- name: glove-index
dimension: 10
metric: DOT
max-degree: 32
ef-construction: 256
use-deduplication: false
Config config = new Config();
VectorCollectionConfig collectionConfig = new VectorCollectionConfig("books")
.setBackupCount(1)
.setAsyncBackupCount(0)
.addVectorIndexConfig(
new VectorIndexConfig()
.setName("word2vec-index")
.setDimension(6)
.setMetric(Metric.DOT)
).addVectorIndexConfig(
new VectorIndexConfig()
.setName("glove-index")
.setDimension(10)
.setMetric(Metric.DOT)
.setMaxDegree(32)
.setEfConstruction(256)
.setUseDeduplication(false)
);
config.addVectorCollectionConfig(collectionConfig);
client.create_vector_collection_config("books", backup_count=1, async_backup_count=0, indexes=[
IndexConfig(name="word2vec-index", metric=Metric.DOT, dimension=6),
IndexConfig(name="glove-index", metric=Metric.DOT, dimension=10,
max_degree=32, ef_construction=256, use_deduplication=False),
])
Create collection
You can use either of the VectorCollection
static methods to get the vector collection. Both methods either create a vector collection, or return an existing one that corresponds to the requested name.
The methods are as follows:
-
getCollection(HazelcastInstance instance, VectorCollectionConfig collectionConfig)
-
If a collection with the provided name does not exist, a new collection is created with the given configuration. If the configuration for the collection already exists, the provided configuration must match the existing configuration; if the configuration does not match, an error is thrown.
-
If a collection with the same name and configuration already exists, it is returned.
-
If a collection with the same name but a different configuration exists, an error is thrown.
-
VectorCollectionConfig collectionConfig = new VectorCollectionConfig("books")
.addVectorIndexConfig(
new VectorIndexConfig()
.setDimension(6)
.setMetric(Metric.DOT)
);
VectorCollection vectorCollection = VectorCollection.getCollection(hazelcastInstance, vectorCollectionConfig);
# create configuration and get collection separately
client.create_vector_collection_config("books", indexes=[
IndexConfig(name=None, metric=Metric.DOT, dimension=6)
])
vectorCollection = client.get_vector_collection("books").blocking()
-
getCollection(HazelcastInstance instance, String collectionName)
.-
If a collection with the provided name does not exist, the system creates the collection with the configuration created explicitly during static or dynamic configuration of the cluster. If the configuration does not exist, an error is thrown.
-
If a collection with the provided name exists, it is returned.
-
VectorCollection vectorCollection = VectorCollection.getCollection(hazelcastInstance, "books");
vectorCollection = client.get_vector_collection("books").blocking()
The Java Vector Collection API is only asynchronous, Python provides both asynchronous and synchronous APIs (using blocking() )
|
Manage data
All methods of VectorCollection
that work with collection data are asynchronous. The result is returned as a CompletionStage
. A collection interacts with entries in the form of documents (VectorDocument
). Each document comprises a value and one or more vectors associated with that value.
When using the asynchronous methods, clients must carefully control the number of requests and their concurrency. A large number of requests can potentially overwhelm both the server and the client by consuming significant heap memory during processing. |
Create document
To create a document, use the static factory methods of the VectorDocument
and VectorValues
classes.
Example document for single-index vector collection:
VectorDocument<String> document = VectorDocument.of(
"{'genre': 'novel', 'year': 1976}",
VectorValues.of(
new float[]{0.2f, 0.9f, -1.2f, 2.2f, 2.2f, 3.0f}
)
);
document = Document(
"{'genre': 'novel', 'year': 1976}",
[
Vector("", Type.DENSE, [0.2, 0.9, -1.2, 2.2, 2.2, 3.0]),
],
)
For multi-index collections, specify the names of the indexes to which the vectors belong:
VectorDocument<String> document = VectorDocument.of(
"{'genre': 'fiction', 'year': 2022}",
VectorValues.of(
Map.of(
"word2vec-index", new float[] {0.2f, 0.9f, -1.2f, 2.2f, 2.2f, 3.0f},
"glove-index", new float[] {2f, 3f, 2f, 10f, -2f}
)
)
);
document = Document(
"{'genre': 'novel', 'year': 1976}",
[
Vector("word2vec-index", Type.DENSE, [0.2, 0.9, -1.2, 2.2, 2.2, 3.0]),
Vector("glove-index", Type.DENSE, [2, 3, 2, 10, -2]),
],
)
Put entries
To put a single document to a vector collection, use the putAsync
, putIfAbsent
or setAsync
method of the VectorCollection
class.
VectorDocument<String> document = VectorDocument.of(
"{'genre': 'novel', 'year': 1976}",
VectorValues.of(new float[] {0.2f, 0.9f, -1.2f, 2.2f, 2.2f, 3.0f})
);
CompletionStage<VectorDocument<String>> result = vectorCollection.putAsync("1", document);
vectorCollection.put("1", Document(
"{'genre': 'novel', 'year': 1976}",
[
Vector("", Type.DENSE, [0.2, 0.9, -1.2, 2.2, 2.2, 3.0]),
],
))
To put several documents to a vector collection, use the putAllAsync
method of the VectorCollection
class.
VectorDocument<String> document1 = VectorDocument.of("{'genre': 'novel', 'year': 1976}", VectorValues.of(new float[] {1.2f, -0.3f, 2.2f, 0.4f, 0.3f, 0.4f}));
VectorDocument<String> document2 = VectorDocument.of("{'genre': 'fiction', 'year': 2022}", VectorValues.of(new float[] {1.2f, -0.3f, 2.2f, 0.4f, 0.3f, -2.0f}));
CompletionStage<Void> result = vectorCollection.putAllAsync(
Map.of("1", document1, "2", document2)
);
vectorCollection.put_all(
{
"1": Document(
"{'genre': 'novel', 'year': 1976}",
[
Vector("", Type.DENSE, [1.2, -0.3, 2.2, 0.4, 0.3, 0.4]),
]),
"2": Document(
"{'genre': 'novel', 'year': 1976}",
[
Vector("", Type.DENSE, [1.2, -0.3, 2.2, 0.4, 0.3, -2.0]),
]),
}
)
Read entries
To get a document from a vector collection, use the getAsync
method of the VectorCollection
class.
Update entries
To update a single entry in a vector collection, use the putAsync
or setAsync
method of the VectorCollection
class.
VectorDocument<String> document = VectorDocument.of("{'genre': 'fiction', 'year': 2022}", VectorValues.of(new float[] {1.2f, -0.3f, 2.2f, 0.4f, 0.3f, 0.4f}));
CompletionStage<Void> result = vectorCollection.setAsync("1", document);
vectorCollection.set("1", Document("{'genre': 'fiction', 'year': 2022}",
[
Vector("", Type.DENSE, [1.2, -0.3, 2.2, 0.4, 0.3, 0.4]),
]
))
When you update an entry, you have to provide both VectorDocument and VectorValues even if only one of them is changed for the entry.
|
Delete entries
To delete a document from a vector collection, use the deleteAsync
or removeAsync
method of the VectorCollection
class.
CompletionStage<Void> resultDelete = vectorCollection.deleteAsync("1");
CompletionStage<VectorDocument<String>> resultRemove = vectorCollection.removeAsync("2");
vectorCollection.delete("1")
vectorCollection.remove("2")
These methods do not delete vectors but do mark them as deleted. This can impact search speed and memory usage. To permanently remove vectors from the index, you must run index optimization after deletion. For further information on running index optimization, see optimize method. |
Similarity search
Vector search returns entries with vectors that are most similar to the query vector, based on specified metrics. Any query consists of a single vector to search and the search options, such as the limit of results to retrieve. For more information on the available options, see Similarity search options.
For a similarity search, use the searchAsync
method of the VectorCollection
.
In a single index vector collection, you do not need to specify the name of the index to search. However, for a multi-index vector collection, you must specify the name of the index to search.
Example for single-index vector collection:
CompletionStage<SearchResults<String, String>> results = vectorCollection.searchAsync(
VectorValues.of(new float[] {0f, 0f, 0.2f, -0.3f, 1.2f, -0.5f}),
SearchOptions.builder()
.limit(5)
.includeVectors()
.includeValue()
.build()
);
results = vectorCollection.search_near_vector(
Vector("", Type.DENSE, [0, 0, 0.2, -0.3, 1.2, -0.5]),
limit=5,
include_value=True,
include_vectors=True,
)
Example for multi-index vector collection:
CompletionStage<SearchResults<String, String>> results = vectorCollection.searchAsync(
VectorValues.of("glove-index", new float[] {0f, 0f, 0.2f, -0.3f, 1.2f, -0.5f}),
SearchOptions.builder()
.limit(5)
.includeVectors()
.includeValue()
.build()
);
results = vectorCollection.search_near_vector(
Vector("glove-index", Type.DENSE, [0, 0, 0.2, -0.3, 1.2, -0.5]),
limit=5,
include_value=True,
include_vectors=True,
)
Similarity search options
Search parameters are passed as a searchOptions
argument to the searchAsync method.
Option | Description | Default |
---|---|---|
limit |
The number of results to return in a search result |
|
includeValue |
Whether to include the user value in the search result.
By default, the user value is not included. To include the user value, set to |
|
includeVectors |
Whether to include the vector values in the search result.
By default, the vector values are not included. To include the vector values, set to |
|
hints |
Extra hints for the search. |
|
Using a larger limit may yield better results than with a smaller limit — for example, the nearest neighbor may only be found with a sufficiently large limit .
This can be unexpected if you are trying to compare search results that use a different limit , since one is not guaranteed to be a subset of another.
You can use hints to fine-tune search precision, especially with smaller limit values.
|
Hint | Description |
---|---|
partitionLimit |
Number of results to fetch from each partition. |
memberLimit |
Number of results to fetch from member in two-stage search. |
singleStage |
Force use of single stage search. |
var options = SearchOptions.builder()
.limit(10)
.includeValue()
.includeVectors()
.hint("partitionLimit", 1)
.build();
Hints allow fine-tuning for some aspects of search execution but are subject to change and may be removed in future versions. |
Manage collection
This section provides additional methods for managing the vector collection.
Optimize collection
An optimization operation could be needed in the following cases:
-
To permanently delete vectors that were marked for removal.
-
After adding a significant number of vectors.
-
When the collection returns fewer vectors than expected.
The optimization operation can be a time-consuming and resource-intensive process, and no mutating operations are allowed during this process. |
Limitations in beta version
As this is a beta version, Vector Collection has some limitations; the most significant of which are as follows:
-
The API could change in future versions
-
The rolling-upgrade compatibility guarantees do not apply for vector collections. You might need to delete existing vector collections before migrating to a future version of Hazelcast
-
Only on-heap storage of vector collections is available