Fixed a search feature that was not included in the revision of the object search mechanism - the ability to search based on an object. Resolves Issue 363.
This commit is contained in:
parent
a533232885
commit
e15d6dfb6e
2 changed files with 53 additions and 35 deletions
|
|
@ -12,6 +12,10 @@ from src.utils.utils import to_unicode, make_iter, string_partial_matching
|
||||||
__all__ = ("ObjectManager",)
|
__all__ = ("ObjectManager",)
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
|
|
||||||
|
# delayed import
|
||||||
|
_OBJATTR = None
|
||||||
|
|
||||||
|
|
||||||
# Try to use a custom way to parse id-tagged multimatches.
|
# Try to use a custom way to parse id-tagged multimatches.
|
||||||
|
|
||||||
_AT_MULTIMATCH_INPUT = utils.variable_from_module(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1))
|
_AT_MULTIMATCH_INPUT = utils.variable_from_module(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1))
|
||||||
|
|
@ -114,7 +118,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
should be a valid location object.
|
should be a valid location object.
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates and Q(objattribute__db_obj__pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates and Q(objattribute__db_obj__pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||||
return self.filter(cand_restriction & Q(objattribute__db_key=attribute_name))
|
return list(self.filter(cand_restriction & Q(objattribute__db_key=attribute_name)))
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_attr_value(self, attribute_name, attribute_value, candidates=None, typeclasses=None):
|
def get_objs_with_attr_value(self, attribute_name, attribute_value, candidates=None, typeclasses=None):
|
||||||
|
|
@ -128,9 +132,17 @@ class ObjectManager(TypedObjectManager):
|
||||||
the internal representation. This is reasonably effective but since Attribute values
|
the internal representation. This is reasonably effective but since Attribute values
|
||||||
cannot be indexed, searching by Attribute key is to be preferred whenever possible.
|
cannot be indexed, searching by Attribute key is to be preferred whenever possible.
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates and Q(db_obj__pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||||
return self.filter(cand_restriction & type_restriction & Q(objattribute__db_key=attribute_name, objattribute__db_value=attribute_value))
|
if isinstance(attribute_value, (basestring, int, float, bool, long)):
|
||||||
|
return self.filter(cand_restriction & type_restriction & Q(objattribute__db_key=attribute_name, objattribute__db_value=attribute_value))
|
||||||
|
else:
|
||||||
|
# We have to loop for safety since the referenced lookup gives deepcopy error if attribute value is an object.
|
||||||
|
global _OBJATTR
|
||||||
|
if not _OBJATTR:
|
||||||
|
from src.objects.models import ObjAttribute as _OBJATTR
|
||||||
|
cands = list(self.filter(cand_restriction & type_restriction & Q(objattribute__db_key=attribute_name)))
|
||||||
|
return [_GA(attr, "db_obj") for attr in _OBJATTR.objects.filter(db_obj__in=cands, db_value=attribute_value)]
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_db_property(self, property_name, candidates=None):
|
def get_objs_with_db_property(self, property_name, candidates=None):
|
||||||
|
|
@ -142,7 +154,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
property_name = "db_%s" % property_name.lstrip('db_')
|
property_name = "db_%s" % property_name.lstrip('db_')
|
||||||
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||||
try:
|
try:
|
||||||
return self.filter(cand_restriction).exclude(Q(property_name=None))
|
return list(self.filter(cand_restriction).exclude(Q(property_name=None)))
|
||||||
except exceptions.FieldError:
|
except exceptions.FieldError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
@ -159,7 +171,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||||
try:
|
try:
|
||||||
return self.filter(cand_restriction & type_restriction & Q(property_name=property_value))
|
return list(self.filter(cand_restriction & type_restriction & Q(property_name=property_value)))
|
||||||
except exceptions.FieldError:
|
except exceptions.FieldError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
@ -182,6 +194,12 @@ class ObjectManager(TypedObjectManager):
|
||||||
candidates - list of candidate objects to restrict on
|
candidates - list of candidate objects to restrict on
|
||||||
typeclasses - list of typeclass path strings to restrict on
|
typeclasses - list of typeclass path strings to restrict on
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(ostring, basestring):
|
||||||
|
if hasattr(ostring, "key"):
|
||||||
|
ostring = ostring.key
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
# build query objects
|
# build query objects
|
||||||
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
|
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
|
||||||
cand_restriction = candidates and Q(pk__in=make_iter(candidates_id)) or Q()
|
cand_restriction = candidates and Q(pk__in=make_iter(candidates_id)) or Q()
|
||||||
|
|
@ -212,7 +230,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
# main search methods and helper functions
|
# main search methods and helper functions
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def object_search(self, ostring,
|
def object_search(self, searchdata,
|
||||||
attribute_name=None,
|
attribute_name=None,
|
||||||
typeclass=None,
|
typeclass=None,
|
||||||
candidates=None,
|
candidates=None,
|
||||||
|
|
@ -222,10 +240,10 @@ class ObjectManager(TypedObjectManager):
|
||||||
Always returns a list.
|
Always returns a list.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
ostring: (str) The string to compare names against. By default (if not attribute_name
|
searchdata: (str or obj) The entity to match for. This is usually a key string but may also be an object itself.
|
||||||
is set), this will search object.key and object.aliases in order. Can also
|
By default (if not attribute_name is set), this will search object.key and object.aliases in order. Can also
|
||||||
be on the form #dbref, which will, if exact=True be matched against primary key.
|
be on the form #dbref, which will, if exact=True be matched against primary key.
|
||||||
attribute_name: (str): Use this named ObjectAttribute to match ostring against, instead
|
attribute_name: (str): Use this named ObjectAttribute to match searchdata against, instead
|
||||||
of the defaults.
|
of the defaults.
|
||||||
typeclass (str or TypeClass): restrict matches to objects having this typeclass. This will help
|
typeclass (str or TypeClass): restrict matches to objects having this typeclass. This will help
|
||||||
speed up global searches.
|
speed up global searches.
|
||||||
|
|
@ -242,20 +260,19 @@ class ObjectManager(TypedObjectManager):
|
||||||
A list of matching objects (or a list with one unique match)
|
A list of matching objects (or a list with one unique match)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def _searcher(ostring, candidates, typeclass, exact=False):
|
def _searcher(searchdata, candidates, typeclass, exact=False):
|
||||||
"Helper method for searching objects. typeclass is only used for global searching (no candidates)"
|
"Helper method for searching objects. typeclass is only used for global searching (no candidates)"
|
||||||
if attribute_name and isinstance(attribute_name, basestring):
|
if attribute_name:
|
||||||
# attribute/property search (always exact).
|
# attribute/property search (always exact).
|
||||||
matches = self.get_objs_with_db_property_value(attribute_name, ostring, candidates=candidates, typeclasses=typeclass)
|
matches = self.get_objs_with_db_property_value(attribute_name, searchdata, candidates=candidates, typeclasses=typeclass)
|
||||||
if matches:
|
if matches:
|
||||||
return matches
|
return matches
|
||||||
return self.get_objs_with_attr_value(attribute_name, ostring, candidates=candidates, typeclasses=typeclass)
|
return self.get_objs_with_attr_value(attribute_name, searchdata, candidates=candidates, typeclasses=typeclass)
|
||||||
else:
|
else:
|
||||||
# normal key/alias search
|
# normal key/alias search
|
||||||
return self.get_objs_with_key_or_alias(ostring, exact=exact, candidates=candidates, typeclasses=typeclass)
|
return self.get_objs_with_key_or_alias(searchdata, exact=exact, candidates=candidates, typeclasses=typeclass)
|
||||||
|
|
||||||
|
if not searchdata and searchdata != 0:
|
||||||
if not ostring and ostring != 0:
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if typeclass:
|
if typeclass:
|
||||||
|
|
@ -272,7 +289,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
if typeclass:
|
if typeclass:
|
||||||
candidates = [cand for cand in candidates if _GA(cand, "db_typeclass_path") in typeclass]
|
candidates = [cand for cand in candidates if _GA(cand, "db_typeclass_path") in typeclass]
|
||||||
|
|
||||||
dbref = not attribute_name and exact and self.dbref(ostring)
|
dbref = not attribute_name and exact and self.dbref(searchdata)
|
||||||
if dbref != None:
|
if dbref != None:
|
||||||
# Easiest case - dbref matching (always exact)
|
# Easiest case - dbref matching (always exact)
|
||||||
dbref_match = self.dbref_search(dbref)
|
dbref_match = self.dbref_search(dbref)
|
||||||
|
|
@ -283,15 +300,14 @@ class ObjectManager(TypedObjectManager):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Search through all possibilities.
|
# Search through all possibilities.
|
||||||
|
|
||||||
match_number = None
|
match_number = None
|
||||||
# always run first check exact - we don't want partial matches if on the form of 1-keyword etc.
|
# always run first check exact - we don't want partial matches if on the form of 1-keyword etc.
|
||||||
matches = _searcher(ostring, candidates, typeclass, exact=True)
|
matches = _searcher(searchdata, candidates, typeclass, exact=True)
|
||||||
if not matches:
|
if not matches:
|
||||||
# no matches found - check if we are dealing with N-keyword query - if so, strip it.
|
# no matches found - check if we are dealing with N-keyword query - if so, strip it.
|
||||||
match_number, ostring = _AT_MULTIMATCH_INPUT(ostring)
|
match_number, searchdata = _AT_MULTIMATCH_INPUT(searchdata)
|
||||||
# run search again, with the exactness set by call
|
# run search again, with the exactness set by call
|
||||||
matches = _searcher(ostring, candidates, typeclass, exact=exact)
|
matches = _searcher(searchdata, candidates, typeclass, exact=exact)
|
||||||
|
|
||||||
# deal with result
|
# deal with result
|
||||||
if len(matches) > 1 and match_number != None:
|
if len(matches) > 1 and match_number != None:
|
||||||
|
|
|
||||||
|
|
@ -546,7 +546,7 @@ class ObjectDB(TypedObject):
|
||||||
# Main Search method
|
# Main Search method
|
||||||
#
|
#
|
||||||
|
|
||||||
def search(self, ostring,
|
def search(self, searchdata,
|
||||||
global_search=False,
|
global_search=False,
|
||||||
use_nicks=False,
|
use_nicks=False,
|
||||||
typeclass=None,
|
typeclass=None,
|
||||||
|
|
@ -564,7 +564,7 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
|
|
||||||
ostring (str): Primary search criterion. Will be matched against object.key (with object.aliases second)
|
searchdata (str or obj): Primary search criterion. Will be matched against object.key (with object.aliases second)
|
||||||
unless the keyword attribute_name specifies otherwise. Special strings:
|
unless the keyword attribute_name specifies otherwise. Special strings:
|
||||||
#<num> - search by unique dbref. This is always a global search.
|
#<num> - search by unique dbref. This is always a global search.
|
||||||
me,self - self-reference to this object
|
me,self - self-reference to this object
|
||||||
|
|
@ -575,7 +575,7 @@ class ObjectDB(TypedObject):
|
||||||
be a list of typeclasses for a broader search.
|
be a list of typeclasses for a broader search.
|
||||||
location (Object): Specify a location to search, if different from the self's given location
|
location (Object): Specify a location to search, if different from the self's given location
|
||||||
plus its contents. This can also be a list of locations.
|
plus its contents. This can also be a list of locations.
|
||||||
attribute_name (str): Use this named Attribute to match ostring against, instead of object.key.
|
attribute_name (str): Use this named Attribute to match searchdata against, instead of object.key.
|
||||||
quiet (bool) - don't display default error messages - return multiple matches as a list and
|
quiet (bool) - don't display default error messages - return multiple matches as a list and
|
||||||
no matches as None. If not set (default), will echo error messages and return None.
|
no matches as None. If not set (default), will echo error messages and return None.
|
||||||
exact (bool) - if unset (default) - prefers to match to beginning of string rather than not matching
|
exact (bool) - if unset (default) - prefers to match to beginning of string rather than not matching
|
||||||
|
|
@ -597,10 +597,12 @@ class ObjectDB(TypedObject):
|
||||||
a unique object match
|
a unique object match
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
is_string = isinstance(searchdata, basestring)
|
||||||
|
|
||||||
# handle some common self-references:
|
# handle some common self-references:
|
||||||
if ostring == _HERE:
|
if searchdata == _HERE:
|
||||||
return self.location
|
return self.location
|
||||||
if ostring in (_ME, _SELF):
|
if searchdata in (_ME, _SELF):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if use_nicks:
|
if use_nicks:
|
||||||
|
|
@ -611,12 +613,12 @@ class ObjectDB(TypedObject):
|
||||||
if self.has_player:
|
if self.has_player:
|
||||||
nicks = list(nicks) + list(PlayerNick.objects.filter(db_obj=self.db_player, db_type=nicktype))
|
nicks = list(nicks) + list(PlayerNick.objects.filter(db_obj=self.db_player, db_type=nicktype))
|
||||||
for nick in nicks:
|
for nick in nicks:
|
||||||
if ostring == nick.db_nick:
|
if searchdata == nick.db_nick:
|
||||||
ostring = nick.db_real
|
searchdata = nick.db_real
|
||||||
break
|
break
|
||||||
|
|
||||||
candidates=None
|
candidates=None
|
||||||
if global_search or (ostring.startswith("#") and len(ostring) > 1 and ostring[1:].isdigit()):
|
if global_search or (is_string and searchdata.startswith("#") and len(searchdata) > 1 and searchdata[1:].isdigit()):
|
||||||
# only allow exact matching if searching the entire database or unique #dbrefs
|
# only allow exact matching if searching the entire database or unique #dbrefs
|
||||||
exact = True
|
exact = True
|
||||||
elif location:
|
elif location:
|
||||||
|
|
@ -635,25 +637,25 @@ class ObjectDB(TypedObject):
|
||||||
# db manager expects database objects
|
# db manager expects database objects
|
||||||
candidates = [obj.dbobj for obj in candidates]
|
candidates = [obj.dbobj for obj in candidates]
|
||||||
|
|
||||||
results = ObjectDB.objects.object_search(ostring,
|
results = ObjectDB.objects.object_search(searchdata,
|
||||||
attribute_name=attribute_name,
|
attribute_name=attribute_name,
|
||||||
typeclass=typeclass,
|
typeclass=typeclass,
|
||||||
candidates=candidates,
|
candidates=candidates,
|
||||||
exact=exact)
|
exact=exact)
|
||||||
if quiet:
|
if quiet:
|
||||||
return results
|
return results
|
||||||
return _AT_SEARCH_RESULT(self, ostring, results, global_search)
|
return _AT_SEARCH_RESULT(self, searchdata, results, global_search)
|
||||||
|
|
||||||
def search_player(self, ostring, quiet=False):
|
def search_player(self, searchdata, quiet=False):
|
||||||
"""
|
"""
|
||||||
Simple wrapper of the player search also handling me, self
|
Simple wrapper of the player search also handling me, self
|
||||||
"""
|
"""
|
||||||
if ostring in (_ME, _SELF) and _GA(self, "db_player"):
|
if searchdata in (_ME, _SELF) and _GA(self, "db_player"):
|
||||||
return _GA(self, "db_player")
|
return _GA(self, "db_player")
|
||||||
results = PlayerDB.objects.player_search(ostring)
|
results = PlayerDB.objects.player_search(searchdata)
|
||||||
if quiet:
|
if quiet:
|
||||||
return results
|
return results
|
||||||
return _AT_SEARCH_RESULT(self, ostring, results, True)
|
return _AT_SEARCH_RESULT(self, searchdata, results, True)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Execution/action methods
|
# Execution/action methods
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue