From 06047a203aaf710bac87cffc56a3eda150d15eec Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:58:15 -0700 Subject: [PATCH] Fix: index root document when a new version is consumed --- src/documents/signals/handlers.py | 5 ++ src/documents/tests/search/test_backend.py | 80 ++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 30c682cbf..641c399b5 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -794,6 +794,11 @@ def cleanup_user_deletion(sender, instance: User | Group, **kwargs) -> None: def add_to_index(sender, document, **kwargs) -> None: from documents.search import get_backend + # A newly consumed version is not searchable on its own, its content + # becomes the effective content of the root document. + if document.root_document_id: + document = document.root_document + get_backend().add_or_update( document, effective_content=document.get_effective_content(), diff --git a/src/documents/tests/search/test_backend.py b/src/documents/tests/search/test_backend.py index b75b58679..360510c8d 100644 --- a/src/documents/tests/search/test_backend.py +++ b/src/documents/tests/search/test_backend.py @@ -16,6 +16,7 @@ from documents.search._backend import TantivyBackend from documents.search._backend import WriteBatch from documents.search._backend import get_backend from documents.search._backend import reset_backend +from documents.signals.handlers import add_to_index from documents.tests.factories import CorrespondentFactory from documents.tests.factories import DocumentFactory from documents.tests.factories import DocumentTypeFactory @@ -1030,6 +1031,85 @@ class TestHighlightHits: assert len(hits) == 0 +class TestVersionIndexing: + """ + GIVEN: + - A root document with a consumed version + WHEN: + - The consumed version is indexed + THEN: + - The root document's index entry is updated to reflect the consumed version's content + """ + + def test_consumed_version_updates_root_entry( + self, + backend: TantivyBackend, + mocker: MockerFixture, + ) -> None: + root = Document.objects.create( + title="Statement", + content="", + checksum="VER1", + pk=90, + ) + backend.add_or_update(root, effective_content=root.get_effective_content()) + version = Document.objects.create( + title="Statement", + content="unprotected statement text", + checksum="VER2", + pk=91, + root_document=root, + ) + mocker.patch("documents.search.get_backend", return_value=backend) + + add_to_index(sender=None, document=version) + + assert backend.search_ids("unprotected", user=None) == [root.pk] + + def test_consumed_version_replaces_previous_content( + self, + backend: TantivyBackend, + mocker: MockerFixture, + ) -> None: + root = Document.objects.create( + title="Statement", + content="stale original text", + checksum="VER3", + pk=92, + ) + backend.add_or_update(root, effective_content=root.get_effective_content()) + version = Document.objects.create( + title="Statement", + content="fresh version text", + checksum="VER4", + pk=93, + root_document=root, + ) + mocker.patch("documents.search.get_backend", return_value=backend) + + add_to_index(sender=None, document=version) + + assert backend.search_ids("fresh", user=None) == [root.pk] + assert backend.search_ids("stale", user=None) == [] + + def test_consumed_root_document_is_indexed_directly( + self, + backend: TantivyBackend, + mocker: MockerFixture, + ) -> None: + root = Document.objects.create( + title="Standalone", + content="standalone document text", + checksum="VER5", + pk=94, + ) + mocker.patch("documents.search.get_backend", return_value=backend) + + add_to_index(sender=None, document=root) + + assert backend.search_ids("standalone", user=None) == [root.pk] + + class TestIndexDirectoryGarbageCollection: """Regression tests for Tantivy segment files leaking on disk when multiple long-lived worker processes (Granian/Celery) take turns writing