1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #10560: New global search (#10676)

* Initial work on new search backend

* Clean up search backends

* Return only the most relevant result per object

* Clear any pre-existing cached entries on cache()

* #6003: Implement global search functionality for custom field values

* Tweak field weights & document guidance

* Extend search() to accept a lookup type

* Move get_registry() out of SearchBackend

* Enforce object permissions when returning search results

* Add indexers for remaining models

* Avoid calling remove() on non-cacheable objects

* Use new search backend by default

* Extend search backend to filter by object type

* Clean up search view form

* Enable specifying lookup logic

* Add indexes for value field

* Remove object type selector from search bar

* Introduce SearchTable and enable HTMX for results

* Enable pagination

* Remove legacy search backend

* Cleanup

* Use a UUID for CachedValue primary key

* Refactoring search methods

* Define max search results limit

* Extend reindex command to support specifying particular models

* Add clear() and size to SearchBackend

* Optimize bulk caching performance

* Highlight matched portion of field value

* Performance improvements for reindexing

* Started on search tests

* Cleanup & docs

* Documentation updates

* Clean up SearchIndex

* Flatten search registry to register by app_label.model_name

* Clean up search backend classes

* Clean up RestrictedGenericForeignKey and RestrictedPrefetch

* Resolve migrations conflict
This commit is contained in:
Jeremy Stretch
2022-10-21 13:16:16 -04:00
committed by GitHub
parent 5d56d95fda
commit 9628dead07
50 changed files with 1579 additions and 675 deletions

View File

@@ -0,0 +1,77 @@
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand, CommandError
from extras.registry import registry
from netbox.search.backends import search_backend
class Command(BaseCommand):
help = 'Reindex objects for search'
def add_arguments(self, parser):
parser.add_argument(
'args',
metavar='app_label[.ModelName]',
nargs='*',
help='One or more apps or models to reindex',
)
def _get_indexers(self, *model_names):
indexers = {}
# No models specified; pull in all registered indexers
if not model_names:
for idx in registry['search'].values():
indexers[idx.model] = idx
# Return only indexers for the specified models
else:
for label in model_names:
try:
app_label, model_name = label.lower().split('.')
except ValueError:
raise CommandError(
f"Invalid model: {label}. Model names must be in the format <app_label>.<model_name>."
)
try:
idx = registry['search'][f'{app_label}.{model_name}']
indexers[idx.model] = idx
except KeyError:
raise CommandError(f"No indexer registered for {label}")
return indexers
def handle(self, *model_labels, **kwargs):
# Determine which models to reindex
indexers = self._get_indexers(*model_labels)
if not indexers:
raise CommandError("No indexers found!")
self.stdout.write(f'Reindexing {len(indexers)} models.')
# Clear all cached values for the specified models
self.stdout.write('Clearing cached values... ', ending='')
self.stdout.flush()
content_types = [
ContentType.objects.get_for_model(model) for model in indexers.keys()
]
deleted_count = search_backend.clear(content_types)
self.stdout.write(f'{deleted_count} entries deleted.')
# Index models
self.stdout.write('Indexing models')
for model, idx in indexers.items():
app_label = model._meta.app_label
model_name = model._meta.model_name
self.stdout.write(f' {app_label}.{model_name}... ', ending='')
self.stdout.flush()
i = search_backend.cache(model.objects.iterator(), remove_existing=False)
if i:
self.stdout.write(f'{i} entries cached.')
else:
self.stdout.write(f'None found.')
msg = f'Completed.'
if total_count := search_backend.size:
msg += f' Total entries: {total_count}'
self.stdout.write(msg, self.style.SUCCESS)