Add MariaDB Vector DocumentStore integration#3565
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Coverage report (tavily)Click to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||
Implements MariaDBDocumentStore backed by MariaDB 11.7+ native VECTOR support with MHNSW indexing and full-text keyword search. - Full DocumentStore protocol: write_documents (FAIL/OVERWRITE/SKIP), filter_documents, delete_documents, count_documents - Vector similarity via VEC_DISTANCE_COSINE / VEC_DISTANCE_EUCLIDEAN - Full-text keyword search via MATCH ... AGAINST (NATURAL LANGUAGE MODE) - Haystack metadata filtering converted to JSON_EXTRACT SQL expressions - MariaDBEmbeddingRetriever and MariaDBKeywordRetriever with FilterPolicy - 80 tests: 68 unit + 12 integration (all verified against MariaDB 11.7) - GitHub Actions workflow with MariaDB 11.7 service container Closes deepset-ai#2340
- Make mariadb C extension import lazy so API reference builds without requiring libmariadb-dev in the docs environment - Fix Docker health check to use --su-mysql flag required by MariaDB 11.7 - Add mariadb (LGPL-2.1) to license compliance exclusion list
f71d1cd to
ff12b6a
Compare
- Add skip-install=true to default hatch env so docs build does not attempt to compile the mariadb C extension (libmariadb-dev not available in the API reference runner). The pydoc search_path already points to src/ so modules are importable without installation. - Switch service container health check from healthcheck.sh (unreliable in some MariaDB 11.7 images) to mysqladmin ping which is more robust.
…THCHECK The mariadb:11.7 Docker image ships with a HEALTHCHECK instruction. GitHub Actions automatically waits for it when no custom options override it. Custom health-cmd variants (healthcheck.sh, mysqladmin) were failing because the slim runner environment handles them differently.
anakin87
left a comment
There was a problem hiding this comment.
I took a first look and found some points to address and some others to discuss.
| @@ -0,0 +1,10 @@ | |||
| # Changelog | |||
There was a problem hiding this comment.
this file is auto-generated and updated at release time.
Remove it.
| @@ -0,0 +1,77 @@ | |||
| # MariaDB Document Store for Haystack | |||
There was a problem hiding this comment.
This should be minimal, like other READMEs in this repo.
See for example https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/pgvector
| # skip-install prevents hatch from installing the project (and thus the mariadb C extension) | ||
| # in the docs environment. The pydoc search_path points to src/ so modules are found directly. |
There was a problem hiding this comment.
mariadb==1.1.14 uses distutils internally, which is removed in Python 3.14 setuptools provides the shim to keep the test env working.
| @@ -0,0 +1,401 @@ | |||
| # SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai> | |||
There was a problem hiding this comment.
For integration tests, we should use classes from haystack.testing.document_store ( DocumentStoreBaseTests and DocumentStoreExtendedTests or their inner classes). Check Pgvector for an example.
| except Exception: | ||
| self._close_connection() | ||
|
|
||
| import mariadb # noqa: PLC0415 |
There was a problem hiding this comment.
mariadb should be imported at the top of the module
There was a problem hiding this comment.
The lazy import is intentional top-level import triggers the C extension which fails on Python 3.14 due to the distutils removal; placing it after the early-return ping check means unit tests with mocked connections never hit it.
| blob_mime_type VARCHAR(255), | ||
| meta JSON, | ||
| FULLTEXT KEY content_ft_idx (content) | ||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
There was a problem hiding this comment.
We should also set the distance function at table creation.
Also, let's make distance not changeable after table creation:
Declare DISTANCE explicitly. The default is euclidean, and a query using a different distance function than the one the index was built for cannot use the index, it falls back to a full table scan.
See https://mariadb.com/docs/server/reference/sql-structure/vectors/create-table-with-vectors
| try: | ||
| self._cursor.execute(sql, row) | ||
| written += 1 | ||
| except mariadb.IntegrityError as e: | ||
| if policy == DuplicatePolicy.FAIL: | ||
| msg = f"Document with id '{doc.id}' already exists" | ||
| raise DuplicateDocumentError(msg) from e |
There was a problem hiding this comment.
try:
self._connection.begin()
self._cursor.executemany(sql, rows)
written = self._cursor.rowcount
self._connection.commit()
except mariadb.IntegrityError as e:
self._connection.rollback()
msg = "Some documents already exist and policy is FAIL"
raise DuplicateDocumentError(msg) from e
except mariadb.Error as e:
self._connection.rollback()
raise DocumentStoreError("Failed to write documents") from eDoing something like this would ensure both atomic writes and exact numbers for DuplicatePolicy.SKIP
| field_name = field.split(".", 1)[1] | ||
| # JSON_UNQUOTE(JSON_EXTRACT(meta, '$.field')) returns the value as a string. | ||
| # Cast to numeric types when the comparison value is numeric. | ||
| base = f"JSON_UNQUOTE(JSON_EXTRACT(meta, '$.{field_name}'))" |
There was a problem hiding this comment.
this might be prone to SQL injection. Let's make it more robust
| {where_clause} | ||
| ORDER BY score DESC | ||
| LIMIT ? | ||
| """ |
There was a problem hiding this comment.
this might also return irrelevant documents if relevant ones are less than top_k
we should also apply matching on WHERE or filtering by score
| def __del__(self) -> None: | ||
| self._close_connection() | ||
|
|
There was a problem hiding this comment.
I'd avoid this. close is sufficient.
|
Addressed all the straightforward review comments. The HNSW index design ( |
Summary
Closes #2340
Implements a complete MariaDB document store integration using MariaDB 11.7+ native
VECTORsupport.MariaDBDocumentStorefull DocumentStore protocol (write_documents,filter_documents,delete_documents,count_documents)VEC_DISTANCE_COSINE/VEC_DISTANCE_EUCLIDEANwithMHNSWindexingMATCH ... AGAINST (IN NATURAL LANGUAGE MODE)on aFULLTEXTindexJSON_UNQUOTE(JSON_EXTRACT(...))SQL expressions with parameterized queriesMariaDBEmbeddingRetrieverandMariaDBKeywordRetrieverwithFilterPolicysupportDuplicatePolicysupport:FAIL(INSERT),OVERWRITE(upsert viaON DUPLICATE KEY UPDATE),SKIP(INSERT IGNORE)Tests
80 tests total all passing:
CI
Added
.github/workflows/mariadb.ymlwith a MariaDB 11.7 service container, matching the pattern used bypgvector.How to run locally