mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-08-30 22:47:15 +00:00
feat: remove effective_content machinery — Document.content is always current
- EffectiveContentFilter and TitleContentFilter simplified to query content directly - Remove FieldError fallback try/except blocks; effective_content annotation gone - add_to_index handler no longer calls get_effective_content() - _build_tantivy_doc and add_or_update drop effective_content parameter - Remove compatibility stubs from versioning.py (EffectiveDocumentResolution, resolve_effective_document_by_pk, get_root_document, get_latest_version_for_root) - Remove get_effective_content() shim from Document model - Remove DocumentVersion.modified compatibility property Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f18b56ed8a
commit
2116ea3329
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db.models import Case
|
||||
from django.db.models import CharField
|
||||
from django.db.models import Count
|
||||
@@ -172,16 +171,10 @@ class TitleContentFilter(Filter):
|
||||
logger.warning(
|
||||
"Deprecated document filter parameter 'title_content' used; use `text` instead.",
|
||||
)
|
||||
try:
|
||||
return qs.filter(
|
||||
Q(title__icontains=value) | Q(effective_content__icontains=value),
|
||||
)
|
||||
except FieldError:
|
||||
return qs.filter(
|
||||
Q(title__icontains=value) | Q(content__icontains=value),
|
||||
)
|
||||
else:
|
||||
return qs
|
||||
return qs.filter(
|
||||
Q(title__icontains=value) | Q(content__icontains=value),
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
@extend_schema_field(serializers.CharField)
|
||||
@@ -190,14 +183,7 @@ class EffectiveContentFilter(Filter):
|
||||
value = value.strip() if isinstance(value, str) else value
|
||||
if not value:
|
||||
return qs
|
||||
try:
|
||||
return qs.filter(
|
||||
**{f"effective_content__{self.lookup_expr}": value},
|
||||
)
|
||||
except FieldError:
|
||||
return qs.filter(
|
||||
**{f"content__{self.lookup_expr}": value},
|
||||
)
|
||||
return qs.filter(**{f"content__{self.lookup_expr}": value})
|
||||
|
||||
|
||||
@extend_schema_field(serializers.BooleanField)
|
||||
|
||||
@@ -336,16 +336,6 @@ class DocumentVersion(DocumentBase):
|
||||
def source_file(self):
|
||||
return self.source_path.open("rb")
|
||||
|
||||
@property
|
||||
def modified(self):
|
||||
"""Compatibility shim for conditionals.py which expects .modified.
|
||||
|
||||
DocumentVersion tracks creation time via added; callers that need a
|
||||
last-modified value for HTTP caching use this property.
|
||||
Task 9 will refactor conditionals.py to stop using this path.
|
||||
"""
|
||||
return self.added
|
||||
|
||||
|
||||
class Document(DocumentBase, SoftDeleteModel, ModelWithOwner): # type: ignore[django-manager-missing]
|
||||
MAX_STORED_FILENAME_LENGTH: Final[int] = 1024
|
||||
@@ -525,15 +515,6 @@ class Document(DocumentBase, SoftDeleteModel, ModelWithOwner): # type: ignore[d
|
||||
def created_date(self):
|
||||
return self.created
|
||||
|
||||
def get_effective_content(self) -> str:
|
||||
"""Return the content to use for search indexing and matching.
|
||||
|
||||
This is a compatibility shim; since DocumentVersion now holds per-version
|
||||
content, the Document.content cache field already reflects the latest version.
|
||||
Task 9 will remove the callers of this method.
|
||||
"""
|
||||
return self.content
|
||||
|
||||
def add_nested_tags(self, tags) -> None:
|
||||
tag_ids = set()
|
||||
for tag in tags:
|
||||
|
||||
@@ -184,7 +184,6 @@ class WriteBatch:
|
||||
def add_or_update(
|
||||
self,
|
||||
document: Document,
|
||||
effective_content: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Add or update a document in the batch.
|
||||
@@ -195,11 +194,9 @@ class WriteBatch:
|
||||
|
||||
Args:
|
||||
document: Django Document instance to index
|
||||
effective_content: Override document.content for indexing (used when
|
||||
re-indexing with newer OCR text from document versions)
|
||||
"""
|
||||
self.remove(document.pk)
|
||||
doc = self._backend._build_tantivy_doc(document, effective_content)
|
||||
doc = self._backend._build_tantivy_doc(document)
|
||||
self._writer.add_document(doc)
|
||||
|
||||
def remove(self, doc_id: int) -> None:
|
||||
@@ -275,16 +272,9 @@ class TantivyBackend:
|
||||
def _build_tantivy_doc(
|
||||
self,
|
||||
document: Document,
|
||||
effective_content: str | None = None,
|
||||
) -> tantivy.Document:
|
||||
"""Build a tantivy Document from a Django Document instance.
|
||||
|
||||
``effective_content`` overrides ``document.content`` for indexing —
|
||||
used when re-indexing a root document with a newer version's OCR text.
|
||||
"""
|
||||
content = (
|
||||
effective_content if effective_content is not None else document.content
|
||||
)
|
||||
"""Build a tantivy Document from a Django Document instance."""
|
||||
content = document.content
|
||||
|
||||
doc = tantivy.Document()
|
||||
|
||||
@@ -395,7 +385,6 @@ class TantivyBackend:
|
||||
def add_or_update(
|
||||
self,
|
||||
document: Document,
|
||||
effective_content: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Add or update a single document with file locking.
|
||||
@@ -405,11 +394,10 @@ class TantivyBackend:
|
||||
|
||||
Args:
|
||||
document: Django Document instance to index
|
||||
effective_content: Override document.content for indexing
|
||||
"""
|
||||
self._ensure_open()
|
||||
with self.batch_update(lock_timeout=5.0) as batch:
|
||||
batch.add_or_update(document, effective_content)
|
||||
batch.add_or_update(document)
|
||||
|
||||
def remove(self, doc_id: int) -> None:
|
||||
"""
|
||||
@@ -805,10 +793,7 @@ class TantivyBackend:
|
||||
try:
|
||||
writer = new_index.writer()
|
||||
for document in iter_wrapper(documents):
|
||||
doc = self._build_tantivy_doc(
|
||||
document,
|
||||
document.get_effective_content(),
|
||||
)
|
||||
doc = self._build_tantivy_doc(document)
|
||||
writer.add_document(doc)
|
||||
writer.commit()
|
||||
new_index.reload()
|
||||
|
||||
@@ -782,10 +782,7 @@ def cleanup_user_deletion(sender, instance: User | Group, **kwargs) -> None:
|
||||
def add_to_index(sender, document, **kwargs) -> None:
|
||||
from documents.search import get_backend
|
||||
|
||||
get_backend().add_or_update(
|
||||
document,
|
||||
effective_content=document.get_effective_content(),
|
||||
)
|
||||
get_backend().add_or_update(document)
|
||||
|
||||
|
||||
def run_workflows_added(
|
||||
|
||||
@@ -41,52 +41,6 @@ def get_version_by_pk(doc: Document, version_pk: int) -> DocumentVersion | None:
|
||||
return DocumentVersion.objects.filter(pk=version_pk, document=doc).first()
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class EffectiveDocumentResolution:
|
||||
"""Compatibility shim for conditionals.py callers that access .document.
|
||||
|
||||
Task 9 will refactor these callers to use VersionResolution.version directly.
|
||||
"""
|
||||
|
||||
document: DocumentVersion | None
|
||||
|
||||
|
||||
def resolve_effective_document_by_pk(
|
||||
pk: int,
|
||||
request: Any,
|
||||
) -> EffectiveDocumentResolution:
|
||||
"""Resolve the effective DocumentVersion by document pk and request params.
|
||||
|
||||
This is a compatibility stub used by conditionals.py; Task 9 will refactor
|
||||
the callers to use resolve_requested_version directly.
|
||||
"""
|
||||
try:
|
||||
doc = Document.objects.get(pk=pk)
|
||||
except Document.DoesNotExist:
|
||||
return EffectiveDocumentResolution(document=None)
|
||||
resolution = resolve_requested_version(doc, request)
|
||||
return EffectiveDocumentResolution(document=resolution.version)
|
||||
|
||||
|
||||
def get_root_document(doc: Document) -> Document:
|
||||
"""Return the root document.
|
||||
|
||||
In the new model, every Document IS the root. This is a compatibility stub
|
||||
used by bulk_edit.py; Task 10 will refactor the callers.
|
||||
"""
|
||||
return doc
|
||||
|
||||
|
||||
def get_latest_version_for_root(doc: Document) -> Document:
|
||||
"""Return the document to use as the version source.
|
||||
|
||||
In the new model, DocumentVersion holds per-version files. This stub
|
||||
returns the document itself so that bulk_edit callers that have not yet
|
||||
been updated to the new model do not crash. Task 10 will replace this.
|
||||
"""
|
||||
return doc
|
||||
|
||||
|
||||
def resolve_requested_version(
|
||||
doc: Document,
|
||||
request: Any,
|
||||
|
||||
Reference in New Issue
Block a user