From f740e73cb4d2086c81116c7e2078653fff130386 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:39:54 -0700 Subject: [PATCH] Fix: rebuild old search indexes and look versions up as their root for similar documents An index built before versions were indexed as their root can still hold version entries under their own id and metadata, and only a rebuild clears them. Bump the schema version so the index is rebuilt on upgrade. more_like_this_ids looked the given id up in the index as is, so asking for documents like a version, as the AI suggestions endpoint can, silently found nothing. It now looks the root up instead. --- src/documents/search/_backend.py | 7 +++++- src/documents/search/_schema.py | 4 +++- src/documents/tests/search/test_backend.py | 28 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/documents/search/_backend.py b/src/documents/search/_backend.py index 930a7f48c..aaa4ed395 100644 --- a/src/documents/search/_backend.py +++ b/src/documents/search/_backend.py @@ -1043,16 +1043,21 @@ class TantivyBackend: excluded from results. Args: - doc_id: Primary key of the reference document + doc_id: Primary key of the reference document, or of one of its + versions user: User for permission filtering (None for no filtering) limit: Maximum number of IDs to return (None = all matching docs) Returns: List of similar document IDs (excluding the original) """ + from documents.versioning import root_document_ids + self._ensure_open() searcher = self._index.searcher() + # Only root documents are indexed, so a version stands for its root + doc_id = next(iter(root_document_ids([doc_id])), doc_id) id_query = tantivy.Query.term_query(self._schema, "id", doc_id) results = searcher.search(id_query, limit=1) diff --git a/src/documents/search/_schema.py b/src/documents/search/_schema.py index 62130c246..cb686eabf 100644 --- a/src/documents/search/_schema.py +++ b/src/documents/search/_schema.py @@ -28,7 +28,9 @@ logger = logging.getLogger("paperless.search") # columns dropped. tantivy compares schemas by ordered field list, so an # index built by v1 rejects every write against the v2 schema. # v3 - barcodes JSON field for stored barcode contents -SCHEMA_VERSION: Final[int] = 3 +# v4 - root documents only. Earlier indexes may hold document versions under +# their own id and metadata, so they are rebuilt without them. +SCHEMA_VERSION: Final[int] = 4 # Present in the index directory from the moment a full rebuild starts until it # finishes. If a rebuild is interrupted it is left behind, so the half-built diff --git a/src/documents/tests/search/test_backend.py b/src/documents/tests/search/test_backend.py index 4350280f7..42c89c7fc 100644 --- a/src/documents/tests/search/test_backend.py +++ b/src/documents/tests/search/test_backend.py @@ -1136,6 +1136,34 @@ class TestMoreLikeThis: assert 150 not in ids assert 151 in ids + def test_more_like_this_ids_seeded_by_version_uses_root( + self, + backend: TantivyBackend, + ) -> None: + """A version is not indexed, so it must be looked up as its root.""" + root = DocumentFactory.create( + title="Important document", + content="financial information report", + ) + version = DocumentFactory.create( + title="Important document", + content="financial information report", + root_document=root, + version_index=1, + ) + other = DocumentFactory.create( + title="Another document", + content="financial information report", + ) + backend.add_or_update(root) + backend.add_or_update(other) + + ids = backend.more_like_this_ids(doc_id=version.pk, user=None) + + assert other.pk in ids + assert root.pk not in ids + assert version.pk not in ids + class TestSingleton: """Test get_backend() and reset_backend() singleton lifecycle."""