|
29 | 29 |
|
30 | 30 | logger = logging.getLogger(__name__) |
31 | 31 |
|
| 32 | + |
| 33 | +def _release_embeddings_table_probe(table: Any) -> None: |
| 34 | + """Close a table handle opened only to resolve embeddings table name. |
| 35 | +
|
| 36 | + ``open_embeddings_table`` returns ``(table, name)``; by-model helpers then |
| 37 | + delegate to ``search_*`` methods that open their own handles by name. The |
| 38 | + probe handle must not be left open (LanceDB native tables support |
| 39 | + ``close()``). |
| 40 | + """ |
| 41 | + if table is not None and hasattr(table, "close"): |
| 42 | + try: |
| 43 | + table.close() |
| 44 | + except Exception: |
| 45 | + pass |
| 46 | + |
| 47 | + |
32 | 48 | # Field name whitelist for filter validation |
33 | 49 | # Derived from all LanceDB table schemas in schema_manager.py |
34 | 50 | _VALID_FILTER_FIELDS = frozenset( |
@@ -877,15 +893,18 @@ def search_vectors_by_model( |
877 | 893 | - metadata: Additional metadata |
878 | 894 | """ |
879 | 895 | _table, table_name = self.open_embeddings_table(model_tag) |
880 | | - return self.search_vectors( |
881 | | - table_name=table_name, |
882 | | - query_vector=query_vector, |
883 | | - top_k=top_k, |
884 | | - filters=filters, |
885 | | - vector_column_name=vector_column_name, |
886 | | - user_id=user_id, |
887 | | - is_admin=is_admin, |
888 | | - ) |
| 896 | + try: |
| 897 | + return self.search_vectors( |
| 898 | + table_name=table_name, |
| 899 | + query_vector=query_vector, |
| 900 | + top_k=top_k, |
| 901 | + filters=filters, |
| 902 | + vector_column_name=vector_column_name, |
| 903 | + user_id=user_id, |
| 904 | + is_admin=is_admin, |
| 905 | + ) |
| 906 | + finally: |
| 907 | + _release_embeddings_table_probe(_table) |
889 | 908 |
|
890 | 909 | def search_fts_by_model( |
891 | 910 | self, |
@@ -916,15 +935,18 @@ def search_fts_by_model( |
916 | 935 | List of search result dictionaries (see search_fts). |
917 | 936 | """ |
918 | 937 | _table, table_name = self.open_embeddings_table(model_tag) |
919 | | - return self.search_fts( |
920 | | - table_name=table_name, |
921 | | - query_text=query_text, |
922 | | - top_k=top_k, |
923 | | - filters=filters, |
924 | | - text_column_name=text_column_name, |
925 | | - user_id=user_id, |
926 | | - is_admin=is_admin, |
927 | | - ) |
| 938 | + try: |
| 939 | + return self.search_fts( |
| 940 | + table_name=table_name, |
| 941 | + query_text=query_text, |
| 942 | + top_k=top_k, |
| 943 | + filters=filters, |
| 944 | + text_column_name=text_column_name, |
| 945 | + user_id=user_id, |
| 946 | + is_admin=is_admin, |
| 947 | + ) |
| 948 | + finally: |
| 949 | + _release_embeddings_table_probe(_table) |
928 | 950 |
|
929 | 951 | # --- Async variants (Phase 1A Option C: Hybrid approach) --- |
930 | 952 |
|
@@ -994,15 +1016,18 @@ async def search_vectors_by_model_async( |
994 | 1016 | - metadata: Additional metadata |
995 | 1017 | """ |
996 | 1018 | _table, table_name = self.open_embeddings_table(model_tag) |
997 | | - return await self.search_vectors_async( |
998 | | - table_name=table_name, |
999 | | - query_vector=query_vector, |
1000 | | - top_k=top_k, |
1001 | | - filters=filters, |
1002 | | - vector_column_name=vector_column_name, |
1003 | | - user_id=user_id, |
1004 | | - is_admin=is_admin, |
1005 | | - ) |
| 1019 | + try: |
| 1020 | + return await self.search_vectors_async( |
| 1021 | + table_name=table_name, |
| 1022 | + query_vector=query_vector, |
| 1023 | + top_k=top_k, |
| 1024 | + filters=filters, |
| 1025 | + vector_column_name=vector_column_name, |
| 1026 | + user_id=user_id, |
| 1027 | + is_admin=is_admin, |
| 1028 | + ) |
| 1029 | + finally: |
| 1030 | + _release_embeddings_table_probe(_table) |
1006 | 1031 |
|
1007 | 1032 | @abstractmethod |
1008 | 1033 | async def search_fts_async( |
@@ -1068,13 +1093,16 @@ async def search_fts_by_model_async( |
1068 | 1093 | DatabaseOperationError: If FTS index is not configured or search fails. |
1069 | 1094 | """ |
1070 | 1095 | _table, table_name = self.open_embeddings_table(model_tag) |
1071 | | - return await self.search_fts_async( |
1072 | | - table_name=table_name, |
1073 | | - query_text=query_text, |
1074 | | - top_k=top_k, |
1075 | | - filters=filters, |
1076 | | - text_column_name=text_column_name, |
1077 | | - ) |
| 1096 | + try: |
| 1097 | + return await self.search_fts_async( |
| 1098 | + table_name=table_name, |
| 1099 | + query_text=query_text, |
| 1100 | + top_k=top_k, |
| 1101 | + filters=filters, |
| 1102 | + text_column_name=text_column_name, |
| 1103 | + ) |
| 1104 | + finally: |
| 1105 | + _release_embeddings_table_probe(_table) |
1078 | 1106 |
|
1079 | 1107 | @abstractmethod |
1080 | 1108 | async def iter_batches_async( |
|
0 commit comments