|
80 | 80 | ScrollStmt, |
81 | 81 | SearchStmt, |
82 | 82 | SearchWith, |
| 83 | + ShowCollectionStmt, |
83 | 84 | ShowCollectionsStmt, |
84 | 85 | ) |
85 | 86 | from .config import QQLConfig |
86 | 87 | from .embedder import CrossEncoderEmbedder, Embedder, SparseEmbedder |
| 88 | +from .exceptions import QQLRuntimeError |
87 | 89 |
|
88 | 90 | _RERANK_FETCH_MULTIPLIER = 4 |
89 | 91 | _HYBRID_PREFETCH_MULTIPLIER = 4 |
90 | 92 | _COLLECTION_VISIBILITY_TIMEOUT_SECONDS = 5.0 |
91 | 93 | _COLLECTION_VISIBILITY_POLL_SECONDS = 0.05 |
92 | | -from .exceptions import QQLRuntimeError |
93 | 94 |
|
94 | 95 |
|
95 | 96 | @dataclass |
@@ -117,6 +118,8 @@ def execute(self, node: ASTNode) -> ExecutionResult: |
117 | 118 | return self._execute_drop(node) |
118 | 119 | if isinstance(node, ShowCollectionsStmt): |
119 | 120 | return self._execute_show(node) |
| 121 | + if isinstance(node, ShowCollectionStmt): |
| 122 | + return self._execute_show_collection(node) |
120 | 123 | if isinstance(node, ScrollStmt): |
121 | 124 | return self._execute_scroll(node) |
122 | 125 | if isinstance(node, SelectStmt): |
@@ -418,6 +421,108 @@ def _execute_show(self, node: ShowCollectionsStmt) -> ExecutionResult: |
418 | 421 | data=names, |
419 | 422 | ) |
420 | 423 |
|
| 424 | + def _execute_show_collection(self, node: ShowCollectionStmt) -> ExecutionResult: |
| 425 | + if not self._client.collection_exists(node.collection): |
| 426 | + raise QQLRuntimeError(f"Collection '{node.collection}' does not exist") |
| 427 | + |
| 428 | + info = self._client.get_collection(node.collection) |
| 429 | + config = info.config |
| 430 | + params = config.params |
| 431 | + |
| 432 | + # ── Vector topology ──────────────────────────────────────────────── |
| 433 | + vectors = params.vectors # type: ignore[union-attr] |
| 434 | + sparse_vector_params = params.sparse_vectors or {} |
| 435 | + if isinstance(vectors, dict): |
| 436 | + vector_details = {} |
| 437 | + for vname, vconfig in vectors.items(): |
| 438 | + vector_details[vname] = { |
| 439 | + "size": vconfig.size, |
| 440 | + "distance": str(vconfig.distance) if vconfig.distance else None, |
| 441 | + } |
| 442 | + elif vectors is None: |
| 443 | + raise QQLRuntimeError( |
| 444 | + f"Collection '{node.collection}' has no vector configuration" |
| 445 | + ) |
| 446 | + else: |
| 447 | + vector_details = { |
| 448 | + "": { |
| 449 | + "size": vectors.size, |
| 450 | + "distance": str(vectors.distance) if vectors.distance else None, |
| 451 | + } |
| 452 | + } |
| 453 | + topology = "hybrid" if sparse_vector_params else "dense" |
| 454 | + |
| 455 | + # ── Sparse vector config ─────────────────────────────────────────── |
| 456 | + sparse_vectors = {} |
| 457 | + if sparse_vector_params: |
| 458 | + for sname, sconfig in sparse_vector_params.items(): |
| 459 | + sparse_vectors[sname] = { |
| 460 | + "modifier": str(sconfig.modifier) if sconfig.modifier else None, |
| 461 | + } |
| 462 | + |
| 463 | + # ── Quantization ─────────────────────────────────────────────────── |
| 464 | + quant_config = config.quantization_config |
| 465 | + quantization = None |
| 466 | + if quant_config is not None: |
| 467 | + qtype = type(quant_config).__name__ |
| 468 | + if hasattr(quant_config, "scalar"): |
| 469 | + quantization = "scalar" |
| 470 | + elif hasattr(quant_config, "binary"): |
| 471 | + quantization = "binary" |
| 472 | + elif hasattr(quant_config, "product"): |
| 473 | + quantization = "product" |
| 474 | + elif hasattr(quant_config, "turbo"): |
| 475 | + quantization = "turbo" |
| 476 | + else: |
| 477 | + quantization = qtype |
| 478 | + |
| 479 | + # ── HNSW config ──────────────────────────────────────────────────── |
| 480 | + hnsw = { |
| 481 | + "m": config.hnsw_config.m, |
| 482 | + "ef_construct": config.hnsw_config.ef_construct, |
| 483 | + } |
| 484 | + if config.hnsw_config.full_scan_threshold is not None: |
| 485 | + hnsw["full_scan_threshold"] = config.hnsw_config.full_scan_threshold |
| 486 | + if config.hnsw_config.max_indexing_threads is not None: |
| 487 | + hnsw["max_indexing_threads"] = config.hnsw_config.max_indexing_threads |
| 488 | + if config.hnsw_config.on_disk is not None: |
| 489 | + hnsw["on_disk"] = config.hnsw_config.on_disk |
| 490 | + if config.hnsw_config.payload_m is not None: |
| 491 | + hnsw["payload_m"] = config.hnsw_config.payload_m |
| 492 | + |
| 493 | + # ── Payload schema / indexes ─────────────────────────────────────── |
| 494 | + payload_indexes = {} |
| 495 | + for field_name, idx_info in (info.payload_schema or {}).items(): |
| 496 | + payload_indexes[field_name] = str(idx_info.data_type) |
| 497 | + |
| 498 | + # ── Sharding / replication ───────────────────────────────────────── |
| 499 | + sharding = { |
| 500 | + "shard_number": params.shard_number, |
| 501 | + "replication_factor": params.replication_factor, |
| 502 | + "write_consistency_factor": params.write_consistency_factor, |
| 503 | + } |
| 504 | + |
| 505 | + data = { |
| 506 | + "name": node.collection, |
| 507 | + "status": str(info.status), |
| 508 | + "points_count": info.points_count, |
| 509 | + "indexed_vectors_count": info.indexed_vectors_count, |
| 510 | + "segments_count": info.segments_count, |
| 511 | + "topology": topology, |
| 512 | + "vectors": vector_details, |
| 513 | + "sparse_vectors": sparse_vectors or None, |
| 514 | + "quantization": quantization, |
| 515 | + "hnsw_config": hnsw, |
| 516 | + "payload_schema": payload_indexes or None, |
| 517 | + "sharding": sharding, |
| 518 | + } |
| 519 | + |
| 520 | + return ExecutionResult( |
| 521 | + success=True, |
| 522 | + message=f"Collection '{node.collection}' diagnostics", |
| 523 | + data=data, |
| 524 | + ) |
| 525 | + |
421 | 526 | def _execute_scroll(self, node: ScrollStmt) -> ExecutionResult: |
422 | 527 | if not self._client.collection_exists(node.collection): |
423 | 528 | raise QQLRuntimeError(f"Collection '{node.collection}' does not exist") |
|
0 commit comments