Skip to content

Commit 678b565

Browse files
committed
Enhance Milvus storage module with advanced indexing options
- Updated example_config.yml and README.md to include detailed vector index settings and options for different index types (IVF_FLAT, IVF_SQ8, IVF_PQ, HNSW, FLAT). - Modified create_collection function in __init__.py to accept and configure index parameters dynamically based on user-defined options. - Improved logging for index creation to provide better insights into the process.
1 parent ba695bf commit 678b565

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

example_config.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,33 @@ storages:
5555
milvus:
5656
module: storage.milvus
5757
options:
58+
# Connection settings
5859
host: "localhost"
5960
port: "19530"
6061
collection_name: "vcons"
62+
63+
# Embedding settings
6164
embedding_model: "text-embedding-3-small"
6265
embedding_dim: 1536
6366
api_key: "your-openai-api-key"
6467
organization: ""
68+
69+
# Operation settings
6570
create_collection_if_missing: true
6671
skip_if_exists: true
72+
73+
# Vector index settings (Default: IVF_FLAT with L2 distance)
74+
index_type: "IVF_FLAT" # Options: IVF_FLAT, IVF_SQ8, IVF_PQ, HNSW, FLAT
75+
metric_type: "L2" # Options: L2, IP, COSINE
76+
nlist: 128 # For IVF indexes: number of clusters
77+
78+
# Advanced HNSW settings (used only if index_type is HNSW)
79+
# m: 16 # Number of edges per node
80+
# ef_construction: 200 # Size of dynamic candidate list during construction
81+
82+
# Advanced IVF_PQ settings (used only if index_type is IVF_PQ)
83+
# pq_m: 8 # Number of sub-quantizers
84+
# pq_nbits: 8 # Bit depth per quantizer
6785
chains:
6886
sample_chain:
6987
links:

server/storage/milvus/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,84 @@ storages:
2929
milvus:
3030
module: storage.milvus
3131
options:
32+
# Connection settings
3233
host: "localhost" # Milvus server host
3334
port: "19530" # Milvus server port
3435
collection_name: "vcons" # Name of the collection in Milvus
36+
37+
# Embedding settings
3538
embedding_model: "text-embedding-3-small" # OpenAI embedding model
3639
embedding_dim: 1536 # Dimensions for the chosen model
3740
api_key: "your-openai-api-key" # Your OpenAI API key
3841
organization: "your-org-id" # Optional: Your OpenAI organization ID
42+
43+
# Operation settings
3944
create_collection_if_missing: true # Whether to create collection if it doesn't exist
4045
skip_if_exists: true # Skip storing vCons that already exist
46+
47+
# Vector index settings (optional, shown are defaults)
48+
index_type: "IVF_FLAT" # Vector index type
49+
metric_type: "L2" # Distance metric type
50+
nlist: 128 # Number of clusters for IVF indexes
4151
```
4252
53+
### Vector Index Types
54+
55+
The module supports different vector index types with appropriate parameters:
56+
57+
#### IVF_FLAT (Default)
58+
Good balance of search accuracy and speed. Uses more storage but gives exact results within each cluster.
59+
60+
```yaml
61+
index_type: "IVF_FLAT"
62+
metric_type: "L2" # Or "IP" for inner product, or "COSINE"
63+
nlist: 128 # Number of clusters, higher values = faster search but less accurate
64+
```
65+
66+
#### IVF_SQ8
67+
Similar to IVF_FLAT but with scalar quantization (8-bit) to reduce memory usage. Good for large datasets.
68+
69+
```yaml
70+
index_type: "IVF_SQ8"
71+
metric_type: "L2"
72+
nlist: 128
73+
```
74+
75+
#### IVF_PQ
76+
Product Quantization for maximum memory optimization. Sacrifices some accuracy for much smaller index size.
77+
78+
```yaml
79+
index_type: "IVF_PQ"
80+
metric_type: "L2"
81+
nlist: 128
82+
pq_m: 8 # Number of sub-quantizers
83+
pq_nbits: 8 # Bit depth per quantizer
84+
```
85+
86+
#### HNSW
87+
Hierarchical Navigable Small World graph index. Very fast for searching, especially with smaller datasets.
88+
89+
```yaml
90+
index_type: "HNSW"
91+
metric_type: "L2"
92+
m: 16 # Number of edges per node
93+
ef_construction: 200 # Size of the dynamic candidate list during construction
94+
```
95+
96+
#### FLAT
97+
The simplest index that compares to every vector. Most accurate but slowest for large datasets.
98+
99+
```yaml
100+
index_type: "FLAT"
101+
metric_type: "L2"
102+
```
103+
104+
### Distance Metrics
105+
106+
- `L2`: Euclidean distance (default). Good for most embeddings including OpenAI embeddings.
107+
- `IP`: Inner product. Use when vectors are normalized and you want to measure closeness.
108+
- `COSINE`: Cosine similarity. Good for measuring the angle between vectors regardless of magnitude.
109+
43110
## Searching vCons in Milvus
44111

45112
While not part of the storage module itself, you can search vCons in Milvus using the pymilvus client:

server/storage/milvus/__init__.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
"organization": None, # OpenAI organization ID
3434
"create_collection_if_missing": False, # Whether to create collection if it doesn't exist
3535
"skip_if_exists": True, # Skip storing vCons that already exist in Milvus
36+
"index_type": "IVF_FLAT", # Vector index type: IVF_FLAT, IVF_SQ8, IVF_PQ, HNSW, ANNOY, etc.
37+
"metric_type": "L2", # Distance metric: L2 (Euclidean), IP (Inner Product), COSINE
38+
"nlist": 128, # Number of clusters for IVF indexes
39+
"m": 16, # HNSW parameter: number of edges per node
40+
"ef_construction": 200, # HNSW parameter: size of the dynamic candidate list during construction
3641
}
3742

3843
def ensure_milvus_connection(host: str, port: str) -> bool:
@@ -266,13 +271,14 @@ def extract_party_id(vcon: dict) -> str:
266271
logger.debug("No usable party identifier found")
267272
return "unknown_party"
268273

269-
def create_collection(collection_name: str, embedding_dim: int) -> Union[Collection, None]:
274+
def create_collection(collection_name: str, embedding_dim: int, opts: dict) -> Union[Collection, None]:
270275
"""
271276
Create a new Milvus collection for vCons.
272277
273278
Args:
274279
collection_name: Name for the new collection
275280
embedding_dim: Dimension of the embedding vectors
281+
opts: Configuration options including index parameters
276282
277283
Returns:
278284
Collection or None: The created collection or None if failed
@@ -301,15 +307,53 @@ def create_collection(collection_name: str, embedding_dim: int) -> Union[Collect
301307
# Create collection
302308
collection = Collection(name=collection_name, schema=schema)
303309

304-
# Create an IVF_FLAT index for fast vector search
310+
# Prepare index parameters based on the selected index type
311+
index_type = opts.get("index_type", "IVF_FLAT")
312+
metric_type = opts.get("metric_type", "L2")
313+
314+
# Configure index parameters based on index type
315+
if index_type.startswith("IVF"): # IVF_FLAT, IVF_SQ8, IVF_PQ
316+
params = {"nlist": opts.get("nlist", 128)}
317+
318+
# Additional params for IVF_PQ
319+
if index_type == "IVF_PQ":
320+
# For PQ, m is typically set to 8 or 12
321+
params["m"] = opts.get("pq_m", 8)
322+
# nbits is typically 8
323+
params["nbits"] = opts.get("pq_nbits", 8)
324+
325+
elif index_type == "HNSW":
326+
params = {
327+
"M": opts.get("m", 16), # Number of edges per node
328+
"efConstruction": opts.get("ef_construction", 200) # Size of the dynamic candidate list during construction
329+
}
330+
331+
elif index_type == "ANNOY":
332+
params = {
333+
"n_trees": opts.get("n_trees", 50) # Number of trees for ANNOY
334+
}
335+
336+
elif index_type == "FLAT":
337+
# FLAT index doesn't need additional parameters
338+
params = {}
339+
340+
else:
341+
# Default to IVF_FLAT if index type is not recognized
342+
logger.warning(f"Unrecognized index type {index_type}, defaulting to IVF_FLAT")
343+
index_type = "IVF_FLAT"
344+
params = {"nlist": opts.get("nlist", 128)}
345+
346+
# Create the index
305347
index_params = {
306-
"metric_type": "L2",
307-
"index_type": "IVF_FLAT",
308-
"params": {"nlist": 128}
348+
"metric_type": metric_type,
349+
"index_type": index_type,
350+
"params": params
309351
}
352+
353+
logger.info(f"Creating index of type {index_type} with metric {metric_type}")
310354
collection.create_index(field_name="embedding", index_params=index_params)
311355

312-
logger.info(f"Created collection '{collection_name}' successfully")
356+
logger.info(f"Created collection '{collection_name}' successfully with {index_type} index")
313357
return collection
314358
except Exception as e:
315359
logger.error(f"Failed to create collection: {e}")
@@ -368,7 +412,7 @@ def save(vcon_uuid: str, opts=default_options) -> None:
368412
if not utility.has_collection(collection_name):
369413
if opts["create_collection_if_missing"]:
370414
logger.info(f"Collection {collection_name} does not exist, creating...")
371-
collection = create_collection(collection_name, opts["embedding_dim"])
415+
collection = create_collection(collection_name, opts["embedding_dim"], opts)
372416
if not collection:
373417
error_msg = f"Failed to create collection {collection_name}"
374418
logger.error(error_msg)

0 commit comments

Comments
 (0)