Skip to content

Commit 03d626a

Browse files
andrewmathew1Andrew Mathew
andauthored
Adding Vector Index Shard Key for DiskANN and quantizedFlat (Azure#39512)
* added vectorIndexShardKey to quantizedFlat and diskANN * added tests for vectorIndexShardKey * added testing for invalid type for vectorIndexShardKey and range of QuantizationByteSize * removed **provisional** from vector embedding policy * added specific index types in readme for vectorIndexShardKey * updated error message for quantizationByteSize out of range --------- Co-authored-by: Andrew Mathew <[email protected]>
1 parent 3c200e5 commit 03d626a

File tree

7 files changed

+167
-15
lines changed

7 files changed

+167
-15
lines changed

sdk/cosmos/azure-cosmos/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,13 +714,16 @@ For vector index types of diskANN and quantizedFlat, there are additional option
714714
quantizationByteSize - the number of bytes used in product quantization of the vectors. A larger value may result in better recall for vector searches at the expense of latency. This applies to index types diskANN and quantizedFlat. The allowed range is between 1 and the minimum between 512 and the vector dimensions. The default value is 64.
715715

716716
indexingSearchListSize - which represents the size of the candidate list of approximate neighbors stored while building the diskANN index as part of the optimization processes. This applies only to index type diskANN. The allowed range is between 25 and 500.
717+
718+
vectorIndexShardKey - a list of strings containing the shard keys used for partitioning vector indexes. The maximum allowed size for this array is 1, meaning that there is only one allowed path. This applies to index types diskANN and quantizedFlat.
717719
```python
718720
indexing_policy = {
719721
"automatic": True,
720722
"indexingMode": "consistent",
721723
"vectorIndexes": [
722724
{"path": "/vector1", "type": "quantizedFlat", "quantizationByteSize": 8},
723-
{"path": "/vector2", "type": "diskANN", "indexingSearchListSize": 50}
725+
{"path": "/vector2", "type": "diskANN", "indexingSearchListSize": 50},
726+
{"path": "/vector3", "type": "diskANN", "vectorIndexShardKey": ["/country/city"]}
724727
]
725728
}
726729
```

sdk/cosmos/azure-cosmos/azure/cosmos/aio/_database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ async def create_container_if_not_exists(
333333
:keyword int analytical_storage_ttl: Analytical store time to live (TTL) for items in the container. A value of
334334
None leaves analytical storage off and a value of -1 turns analytical storage on with no TTL. Please
335335
note that analytical storage can only be enabled on Synapse Link enabled accounts.
336-
:keyword Dict[str, Any] vector_embedding_policy: **provisional** The vector embedding policy for the container.
336+
:keyword Dict[str, Any] vector_embedding_policy: The vector embedding policy for the container.
337337
Each vector embedding possesses a predetermined number of dimensions, is associated with an underlying
338338
data type, and is generated for a particular distance function.
339339
:keyword Dict[str, Any] change_feed_policy: The change feed policy to apply 'retentionDuration' to

sdk/cosmos/azure-cosmos/azure/cosmos/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def create_container( # pylint:disable=docstring-missing-param
204204
:keyword List[Dict[str, str]] computed_properties: Sets The computed properties for this
205205
container in the Azure Cosmos DB Service. For more Information on how to use computed properties visit
206206
`here: https://learn.microsoft.com/azure/cosmos-db/nosql/query/computed-properties?tabs=dotnet`
207-
:keyword Dict[str, Any] vector_embedding_policy: **provisional** The vector embedding policy for the container.
207+
:keyword Dict[str, Any] vector_embedding_policy: The vector embedding policy for the container.
208208
Each vector embedding possesses a predetermined number of dimensions, is associated with an underlying
209209
data type, and is generated for a particular distance function.
210210
:keyword Dict[str, Any] change_feed_policy: The change feed policy to apply 'retentionDuration' to

sdk/cosmos/azure-cosmos/samples/index_management.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ def use_vector_embedding_policy(db):
689689
indexing_policy = {
690690
"vectorIndexes": [
691691
{"path": "/vector", "type": "quantizedFlat", "quantizationByteSize": 8},
692-
{"path": "/vector2", "type": "diskANN", "indexingSearchListSize": 50}
692+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": ["/city"], "indexingSearchListSize": 50}
693693
]
694694
}
695695
vector_embedding_policy = {

sdk/cosmos/azure-cosmos/samples/index_management_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ async def use_vector_embedding_policy(db):
684684
indexing_policy = {
685685
"vectorIndexes": [
686686
{"path": "/vector", "type": "quantizedFlat", "quantizationByteSize": 8},
687-
{"path": "/vector2", "type": "diskANN", "indexingSearchListSize": 50}
687+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": ["/city"], "indexingSearchListSize": 50}
688688
]
689689
}
690690
vector_embedding_policy = {

sdk/cosmos/azure-cosmos/test/test_vector_policy.py

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_create_vector_embedding_container(self):
4040
"vectorIndexes": [
4141
{"path": "/vector1", "type": "flat"},
4242
{"path": "/vector2", "type": "quantizedFlat", "quantizationByteSize": 8},
43-
{"path": "/vector3", "type": "diskANN", "quantizationByteSize": 8, "indexingSearchListSize": 50}
43+
{"path": "/vector3", "type": "diskANN", "quantizationByteSize": 8, "vectorIndexShardKey": ["/city"], "indexingSearchListSize": 50}
4444
]
4545
}
4646
vector_embedding_policy = {
@@ -77,6 +77,23 @@ def test_create_vector_embedding_container(self):
7777
assert properties["indexingPolicy"]["vectorIndexes"] == indexing_policy["vectorIndexes"]
7878
self.test_db.delete_container(container_id)
7979

80+
# Pass a vector indexing policy with hierarchical vectorIndexShardKey value
81+
indexing_policy = {
82+
"vectorIndexes": [
83+
{"path": "/vector2", "type": "diskANN", 'quantizationByteSize': 64, 'indexingSearchListSize': 100, "vectorIndexShardKey": ["/country/city"]}]
84+
}
85+
container_id = "vector_container" + str(uuid.uuid4())
86+
created_container = self.test_db.create_container(
87+
id=container_id,
88+
partition_key=PartitionKey(path="/id"),
89+
indexing_policy=indexing_policy,
90+
vector_embedding_policy=vector_embedding_policy
91+
)
92+
properties = created_container.read()
93+
assert properties["vectorEmbeddingPolicy"] == vector_embedding_policy
94+
assert properties["indexingPolicy"]["vectorIndexes"] == indexing_policy["vectorIndexes"]
95+
self.test_db.delete_container(container_id)
96+
8097
def test_fail_create_vector_indexing_policy(self):
8198
vector_embedding_policy = {
8299
"vectorEmbeddings": [
@@ -85,7 +102,15 @@ def test_fail_create_vector_indexing_policy(self):
85102
"dataType": "float32",
86103
"dimensions": 256,
87104
"distanceFunction": "euclidean"
88-
}]}
105+
},
106+
{
107+
"path": "/vector2",
108+
"dataType": "int8",
109+
"dimensions": 200,
110+
"distanceFunction": "dotproduct"
111+
}
112+
]
113+
}
89114

90115
# Pass a vector indexing policy without embedding policy
91116
indexing_policy = {
@@ -123,7 +148,7 @@ def test_fail_create_vector_indexing_policy(self):
123148
# Pass a vector indexing policy with non-matching path
124149
indexing_policy = {
125150
"vectorIndexes": [
126-
{"path": "/vector2", "type": "flat"}]
151+
{"path": "/vector3", "type": "flat"}]
127152
}
128153
try:
129154
self.test_db.create_container(
@@ -135,7 +160,7 @@ def test_fail_create_vector_indexing_policy(self):
135160
pytest.fail("Container creation should have failed for index mismatch.")
136161
except exceptions.CosmosHttpResponseError as e:
137162
assert e.status_code == 400
138-
assert "vector2 not matching in Embedding's path" in e.http_error_message
163+
assert "vector3 not matching in Embedding's path" in e.http_error_message
139164

140165
# Pass a vector indexing policy with wrong quantizationByteSize value
141166
indexing_policy = {
@@ -152,7 +177,7 @@ def test_fail_create_vector_indexing_policy(self):
152177
pytest.fail("Container creation should have failed for value mismatch.")
153178
except exceptions.CosmosHttpResponseError as e:
154179
assert e.status_code == 400
155-
assert "QuantizationByteSize value :: 0 is out of range. The allowed range is between 1 and 256."\
180+
assert "The Vector Indexing Policy parameter QuantizationByteSize value :: 0 is out of range. The allowed range is between 1 and 256."\
156181
in e.http_error_message
157182

158183
# Pass a vector indexing policy with wrong indexingSearchListSize value
@@ -173,6 +198,85 @@ def test_fail_create_vector_indexing_policy(self):
173198
assert "IndexingSearchListSize value :: 5 is out of range. The allowed range is between 25 and 500."\
174199
in e.http_error_message
175200

201+
# Pass a vector indexing policy with wrong vectorIndexShardKey value
202+
indexing_policy = {
203+
"vectorIndexes": [
204+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": ["country"]}]
205+
}
206+
try:
207+
self.test_db.create_container(
208+
id='vector_container',
209+
partition_key=PartitionKey(path="/id"),
210+
indexing_policy=indexing_policy,
211+
vector_embedding_policy=vector_embedding_policy
212+
)
213+
pytest.fail("Container creation should have failed for value mismatch.")
214+
except exceptions.CosmosHttpResponseError as e:
215+
assert e.status_code == 400
216+
assert "The Vector Indexing Policy has an invalid Shard Path: country." in e.http_error_message
217+
218+
# Pass a vector indexing policy with too many shard paths
219+
indexing_policy = {
220+
"vectorIndexes": [
221+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": ["/country", "/city", "/zipcode"]}]
222+
}
223+
try:
224+
self.test_db.create_container(
225+
id='vector_container',
226+
partition_key=PartitionKey(path="/id"),
227+
indexing_policy=indexing_policy,
228+
vector_embedding_policy=vector_embedding_policy
229+
)
230+
pytest.fail("Container creation should have failed for value mismatch.")
231+
except exceptions.CosmosHttpResponseError as e:
232+
assert e.status_code == 400
233+
assert "The number of shard paths defined in the Vector Indexing Policy: 3 exceeds the maximum: 1." \
234+
in e.http_error_message
235+
236+
# Pass a vector indexing policy with an invalid type for vectorIndexShardKey
237+
indexing_policy = {
238+
"vectorIndexes": [
239+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": "/country"}]
240+
}
241+
try:
242+
self.test_db.create_container(
243+
id='vector_container',
244+
partition_key=PartitionKey(path="/id"),
245+
indexing_policy=indexing_policy,
246+
vector_embedding_policy=vector_embedding_policy
247+
)
248+
pytest.fail("Container creation should have failed for value mismatch.")
249+
except exceptions.CosmosHttpResponseError as e:
250+
assert e.status_code == 400
251+
assert "One of the specified inputs is invalid" \
252+
in e.http_error_message
253+
254+
# Pass a vector indexing policy with dimensions above 512 to test the max range of "quantizationByteSize"
255+
vector_embedding_policy = {
256+
"vectorEmbeddings": [
257+
{
258+
"path": "/vector1",
259+
"dataType": "float32",
260+
"dimensions": 550,
261+
"distanceFunction": "euclidean"
262+
}]}
263+
indexing_policy = {
264+
"vectorIndexes": [
265+
{"path": "/vector1", "type": "quantizedFlat", "quantizationByteSize": 513 }]
266+
}
267+
try:
268+
self.test_db.create_container(
269+
id='vector_container',
270+
partition_key=PartitionKey(path="/id"),
271+
indexing_policy=indexing_policy,
272+
vector_embedding_policy=vector_embedding_policy
273+
)
274+
pytest.fail("Container creation should have failed for value mismatch.")
275+
except exceptions.CosmosHttpResponseError as e:
276+
assert e.status_code == 400
277+
assert "The Vector Indexing Policy parameter QuantizationByteSize value :: 513 is out of range." \
278+
in e.http_error_message
279+
176280
def test_fail_replace_vector_indexing_policy(self):
177281
vector_embedding_policy = {
178282
"vectorEmbeddings": [

sdk/cosmos/azure-cosmos/test/test_vector_policy_async.py

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ async def test_create_vector_embedding_container_async(self):
5050
indexing_policy = {
5151
"vectorIndexes": [
5252
{"path": "/vector1", "type": "flat"},
53-
{"path": "/vector2", "type": "quantizedFlat", "quantizationByteSize": 64},
53+
54+
{"path": "/vector2", "type": "quantizedFlat", "quantizationByteSize": 64, "vectorIndexShardKey": ["/city"]},
55+
5456
{"path": "/vector3", "type": "diskANN", "quantizationByteSize": 8, "indexingSearchListSize": 50}
5557
]
5658
}
@@ -96,7 +98,15 @@ async def test_fail_create_vector_indexing_policy_async(self):
9698
"dataType": "float32",
9799
"dimensions": 256,
98100
"distanceFunction": "euclidean"
99-
}]}
101+
},
102+
{
103+
"path": "/vector2",
104+
"dataType": "int8",
105+
"dimensions": 200,
106+
"distanceFunction": "dotproduct"
107+
}
108+
]
109+
}
100110

101111
# Pass a vector indexing policy without embedding policy
102112
indexing_policy = {
@@ -134,7 +144,7 @@ async def test_fail_create_vector_indexing_policy_async(self):
134144
# Pass a vector indexing policy with non-matching path
135145
indexing_policy = {
136146
"vectorIndexes": [
137-
{"path": "/vector2", "type": "flat"}]
147+
{"path": "/vector3", "type": "flat"}]
138148
}
139149
try:
140150
await self.test_db.create_container(
@@ -146,7 +156,7 @@ async def test_fail_create_vector_indexing_policy_async(self):
146156
pytest.fail("Container creation should have failed for index mismatch.")
147157
except exceptions.CosmosHttpResponseError as e:
148158
assert e.status_code == 400
149-
assert "vector2 not matching in Embedding's path" in e.http_error_message
159+
assert "vector3 not matching in Embedding's path" in e.http_error_message
150160

151161
# Pass a vector indexing policy with wrong quantizationByteSize value
152162
indexing_policy = {
@@ -163,7 +173,7 @@ async def test_fail_create_vector_indexing_policy_async(self):
163173
pytest.fail("Container creation should have failed for value mismatch.")
164174
except exceptions.CosmosHttpResponseError as e:
165175
assert e.status_code == 400
166-
assert "QuantizationByteSize value :: 0 is out of range. The allowed range is between 1 and 256." \
176+
assert "The Vector Indexing Policy parameter QuantizationByteSize value :: 0 is out of range. The allowed range is between 1 and 256." \
167177
in e.http_error_message
168178

169179
# Pass a vector indexing policy with wrong indexingSearchListSize value
@@ -184,6 +194,41 @@ async def test_fail_create_vector_indexing_policy_async(self):
184194
assert "IndexingSearchListSize value :: 5 is out of range. The allowed range is between 25 and 500." \
185195
in e.http_error_message
186196

197+
# Pass a vector indexing policy with wrong vectorIndexShardKey value
198+
indexing_policy = {
199+
"vectorIndexes": [
200+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": ["country"]}]
201+
}
202+
try:
203+
await self.test_db.create_container(
204+
id='vector_container',
205+
partition_key=PartitionKey(path="/id"),
206+
indexing_policy=indexing_policy,
207+
vector_embedding_policy=vector_embedding_policy
208+
)
209+
pytest.fail("Container creation should have failed for value mismatch.")
210+
except exceptions.CosmosHttpResponseError as e:
211+
assert e.status_code == 400
212+
assert "The Vector Indexing Policy has an invalid Shard Path: country." in e.http_error_message
213+
214+
# Pass a vector indexing policy with too many shard paths
215+
indexing_policy = {
216+
"vectorIndexes": [
217+
{"path": "/vector2", "type": "diskANN", "vectorIndexShardKey": ["/country", "/city", "/zipcode"]}]
218+
}
219+
try:
220+
await self.test_db.create_container(
221+
id='vector_container',
222+
partition_key=PartitionKey(path="/id"),
223+
indexing_policy=indexing_policy,
224+
vector_embedding_policy=vector_embedding_policy
225+
)
226+
pytest.fail("Container creation should have failed for value mismatch.")
227+
except exceptions.CosmosHttpResponseError as e:
228+
assert e.status_code == 400
229+
assert "The number of shard paths defined in the Vector Indexing Policy: 3 exceeds the maximum: 1." \
230+
in e.http_error_message
231+
187232
async def test_fail_replace_vector_indexing_policy_async(self):
188233
vector_embedding_policy = {
189234
"vectorEmbeddings": [

0 commit comments

Comments
 (0)