Coverage for documents/management/commands/document_index.py: 0%

37 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 09:07 +0000

1import logging 

2 

3from django.conf import settings 

4from django.db import transaction 

5 

6from documents.management.commands.base import PaperlessCommand 

7from documents.models import Document 

8from documents.search import get_backend 

9from documents.search import needs_rebuild 

10from documents.search import reset_backend 

11from documents.search import wipe_index 

12 

13logger = logging.getLogger("paperless.management.document_index") 

14 

15 

16class Command(PaperlessCommand): 

17 """ 

18 Django management command for search index operations. 

19 

20 Provides subcommands for reindexing documents and optimizing the search index. 

21 Supports conditional reindexing based on schema version and language changes. 

22 """ 

23 

24 help = "Manages the document index." 

25 

26 supports_progress_bar = True 

27 supports_multiprocessing = False 

28 

29 def add_arguments(self, parser): 

30 super().add_arguments(parser) 

31 parser.add_argument("command", choices=["reindex", "optimize"]) 

32 parser.add_argument( 

33 "--recreate", 

34 action="store_true", 

35 default=False, 

36 help="Wipe and recreate the index from scratch (only used with reindex).", 

37 ) 

38 parser.add_argument( 

39 "--if-needed", 

40 action="store_true", 

41 default=False, 

42 help=( 

43 "Skip reindex if the index is already up to date. " 

44 "Checks schema version and search language sentinels. " 

45 "Safe to run on every startup or upgrade." 

46 ), 

47 ) 

48 parser.add_argument( 

49 "--heap-size-mb", 

50 type=int, 

51 default=None, 

52 help=( 

53 "Tantivy writer memory budget in MB for a full reindex " 

54 "(split across writer threads). Defaults to 512MB; lower " 

55 "this on memory-constrained hosts. Larger values buffer " 

56 "more documents before flushing a segment, deferring " 

57 "merge work rather than avoiding it." 

58 ), 

59 ) 

60 

61 def handle(self, *args, **options): 

62 with transaction.atomic(): 

63 if options["command"] == "reindex": 

64 if options.get("if_needed") and not needs_rebuild(settings.INDEX_DIR): 

65 self.stdout.write("Search index is up to date.") 

66 return 

67 if options.get("recreate"): 

68 wipe_index(settings.INDEX_DIR) 

69 

70 documents = Document.objects.select_related( 

71 "correspondent", 

72 "document_type", 

73 "storage_path", 

74 "owner", 

75 ).prefetch_related( 

76 "tags", 

77 "notes__user", 

78 "custom_fields__field", 

79 "versions", 

80 "barcodes", 

81 "versions__barcodes", 

82 ) 

83 total = documents.count() 

84 rebuild_kwargs = {} 

85 if options.get("heap_size_mb") is not None: 

86 rebuild_kwargs["writer_heap_bytes"] = ( 

87 options["heap_size_mb"] * 1_000_000 

88 ) 

89 get_backend().rebuild( 

90 documents, 

91 iter_wrapper=lambda pairs: self.track( 

92 pairs, 

93 description="Indexing documents...", 

94 total=total, 

95 ), 

96 **rebuild_kwargs, 

97 ) 

98 reset_backend() 

99 

100 elif options["command"] == "optimize": 

101 logger.info( 

102 "document_index optimize is a no-op — Tantivy manages " 

103 "segment merging automatically.", 

104 )