diff --git a/CHANGELOG.md b/CHANGELOG.md index c0cca561fb..28e672077f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -218,6 +218,7 @@ Increase requirements: Django 4.1+, Twisted 22.10+ Python 3.9, 3.10, 3.11. Post - `utils.justify` now supports `align="a"` (absolute alignments. This keeps the given left indent but crops/fills to the width. Used in EvCells. - `EvTable` now supports passing `EvColumn`s as a list directly, (`EvTable(table=[colA,colB])`) +- Add `tags=` search criterion to `DefaultObject.search`. ## Evennia 0.9.5 diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index 7c49ac9547..81e61482a7 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -6,7 +6,6 @@ import re from django.conf import settings from django.db.models import Q from django.db.models.fields import exceptions - from evennia.server import signals from evennia.typeclasses.managers import TypeclassManager, TypedObjectManager from evennia.utils.utils import ( @@ -50,7 +49,7 @@ class ObjectDBManager(TypedObjectManager): get_objs_with_db_property_match get_objs_with_key_or_alias get_contents - object_search (interface to many of the above methods, + search_object (interface to many of the above methods, equivalent to evennia.search_object) copy_object @@ -375,6 +374,7 @@ class ObjectDBManager(TypedObjectManager): candidates=None, exact=True, use_dbref=True, + tags=None, ): """ Search as an object globally or in a list of candidates and @@ -410,6 +410,9 @@ class ObjectDBManager(TypedObjectManager): searching for attributes/properties. use_dbref (bool): If False, bypass direct lookup of a string on the form #dbref and treat it like any string. + tags (list): A list of tuples `(tagkey, tagcategory)` where the + matched object must have _all_ tags in order to be considered + a match. Returns: matches (list): Matching objects @@ -437,7 +440,20 @@ class ObjectDBManager(TypedObjectManager): searchdata, exact=exact, candidates=candidates, typeclasses=typeclass ) + def _search_by_tag(query, taglist): + if not query: + query = self.all() + + for tagkey, tagcategory in taglist: + query = query.filter(db_tags__db_key=tagkey, db_tags__db_category=tagcategory) + + return query + if not searchdata and searchdata != 0: + + if tags: + return _search_by_tag(make_iter(tags)) + return self.none() if typeclass: @@ -477,6 +493,7 @@ class ObjectDBManager(TypedObjectManager): # always run first check exact - we don't want partial matches # if on the form of 1-keyword etc. matches = _searcher(searchdata, candidates, typeclass, exact=True) + if not matches: # no matches found - check if we are dealing with N-keyword # query - if so, strip it. @@ -496,6 +513,9 @@ class ObjectDBManager(TypedObjectManager): elif not exact: matches = _searcher(searchdata, candidates, typeclass, exact=False) + if tags: + matches = _search_by_tag(matches, make_iter(tags)) + # deal with result if len(matches) == 1 and match_number is not None and match_number != 0: # this indicates trying to get a single match with a match-number diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 1886ff62ba..f23393a44c 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -13,7 +13,6 @@ from collections import defaultdict import inflect from django.conf import settings from django.utils.translation import gettext as _ - from evennia.commands import cmdset from evennia.commands.cmdsethandler import CmdSetHandler from evennia.objects.manager import ObjectManager @@ -330,6 +329,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): nofound_string=None, multimatch_string=None, use_dbref=None, + tags=None, stacked=0, ): """ @@ -393,6 +393,8 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): will be treated like a normal string. If `None` (default), the ability to query by #dbref is turned on if `self` has the permission 'Builder' and is turned off otherwise. + tags (list or tuple): Find objects matching one or more Tags. This should be one or + more tag definitions on the form `tagname` or `(tagname, tagcategory)`. stacked (int, optional): If > 0, multimatches will be analyzed to determine if they only contains identical objects; these are then assumed 'stacked' and no multi-match error will be generated, instead `stacked` number of matches will be returned. If @@ -462,13 +464,16 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): # included in location.contents candidates.append(self) - results = ObjectDB.objects.object_search( + tags = [(tagkey, tagcat[0] if tagcat else None) for tagkey, *tagcat in make_iter(tags)] + + results = ObjectDB.objects.search_object( searchdata, attribute_name=attribute_name, typeclass=typeclass, candidates=candidates, exact=exact, use_dbref=use_dbref, + tags=tags, ) if use_locks: