On the `DefaultObject` is a `.search` method which we have already tried out when we made Commands. For this to be used you must already have an object available, and if you are using `py` you can use yourself:
- This searches by `key` or `alias` of the object. Strings are always case-insensitive, so searching for `"rose"`, `"Rose"` or `"rOsE"` give the same results.
- By default it will always search for objects among those in `obj.location.contents` and `obj.contents` (that is, things in obj's inventory or in the same room).
- It will always return exactly one match. If it found zero or more than one match, the return is `None`. This is different from `evennia.search` (see below), which always returns a list.
- On a no-match or multimatch, `.search` will automatically send an error message to `obj`. So you don't have to worry about reporting messages if the result is `None`.
If you want to test this command out, add it to the default cmdset (see [the Command tutorial](./Beginner-Tutorial-Adding-Commands.md) for more details) and then reload the server with `reload`:
```python
# in mygame/commands/default_cmdsets.py
# ...
from commands.command import CmdQuickFind # <-------
class CharacterCmdSet(default_cmds.CharacterCmdSet):
This simple little Command takes its arguments and searches for a match. If it can't find it, `result` will be `None`. The error has already been reported to `self.caller` so we just abort with `return`.
This will only return a match if "MyStone" is in the room (or in your inventory) _and_ is one of the four provided candidate objects. This is quite powerful, here's how you'd find something only in your inventory:
With `quiet=True` the user will not be notified on zero or multi-match errors. Instead you are expected to handle this yourself. Furthermore, what is returned is now a list of zero, one or more matches!
The base search tools of Evennia are the `evennia.search_*` functions, such as `evennia.search_object`. These are normally used in your code, but you can also try them out in-game using `py`:
> py evennia.search_object("rose")
<Queryset[Rose]>
```{sidebar} Querysets
What is returned from the main search functions is actually a `queryset`. They can be treated like lists except that they can't modified in-place. We'll discuss querysets in the [next lesson](./Beginner-Tutorial-Django-queries.md)
```
This searches for objects based on `key` or `alias`. The `.search` method we talked about in the previous section in fact wraps `evennia.search_object` and handles its output in various ways. Here's the same example in Python code, for example as part of a command or coded system:
```python
import evennia
roses = evennia.search_object("rose")
accts = evennia.search_account("YourName")
```
Above we find first the rose and then an Account. You can try both using `py`:
> py evennia.search_object("rose")[0]
Rose
> py evennia.search_account("YourName")[0]
<Player:YourName>
In the example above we used `[0]` to only get the first match of the queryset, which in this case gives us the rose and your Account respectively. Note that if you don't find any matches, using `[0]` like this leads to an error, so it's mostly useful for debugging.
If you you really want all matches to the search parameters you specify. In other situations, having zero or more than one match is a sign of a problem and you need to handle this case yourself. This is too detailed for testing out just with `py`, but good to know if you want to make your own search methods:
```python
the_one_ring = evennia.search_object("The one Ring")
if not the_one_ring:
# handle not finding the ring at all
elif len(the_one_ring) > 1:
# handle finding more than one ring
else:
# ok - exactly one ring found
the_one_ring = the_one_ring[0]
```
There are equivalent search functions for all the main resources. You can find a listing of them [in the Search functions section](../../../Evennia-API.md) of the API front page.
Objects and Accounts can have any number of aliases. When searching for `key` these will searched too, you can't easily search only for aliases. Let's add an alias to our rose with the default `alias` command:
Only Objects (things inheriting from `evennia.DefaultObject`) has a `.location` property.
The `Object.search` method will automatically limit its search by the object's location, so assuming you are in the same room as the rose, this will work:
Let's make another location and move to it - you will no longer find the rose:
> tunnel n = kitchen
north
> py self.search("rose")
Could not find "rose"
However, using `search_object` will find the rose wherever it's located:
> py evennia.search_object("rose")
<QuerySet[Rose]>
However, if you demand that the room is in the current room, it won't be found:
> py evennia.search_object("rose", location=here)
<QuerySet[]>
In general, the `Object.search` is a shortcut for doing the very common searches of things in the same location, whereas the `search_object` finds objects anywhere.
Think of a [Tag](../../../Components/Tags.md) as the label the airport puts on your luggage when flying. Everyone going on the same plane gets a tag, grouping them together so the airport can know what should go to which plane. Entities in Evennia can be grouped in the same way. Any number of tags can be attached to each object.
Tags can also have categories. By default this category is `None` , which is considered a category of its own. Here are some examples of using categories in plain Python code (you can also try this out with `py` if you want to create the objects first):
Note that if you specify the tag with a category, you _must_ also include its category when searching, otherwise the tag-category of `None` will be searched.
> Searching by Attribute can be very practical. But if you want to group entities or search very often, using Tags and search by Tags is faster and more resource-efficient.
Sometimes it's useful to find all objects of a specific Typeclass. All of Evennia's search tools support this. If you were to have a custom typeclass for your `Rose`, you could search for it like this (in Python code):
This last way of searching is a simple form of a Django _query_. This is a way to express SQL queries using Python. See [the next lesson](./Beginner-Tutorial-Django-queries.md), where we'll explore this way to searching in more detail.
Since dbrefs are not reused, do you need to worry about your database ids 'running out' in the future? [No, and here's why](../../../Components/Typeclasses.md#will-i-run-out-of-dbrefs).
```
The database id or `#dbref` is unique and never-reused within each database table. In search methods you can replace the search for `key` with the dbref to search for. This must be written as a string `#dbref`:
In legacy code bases you may be used to relying a lot on #dbrefs to find and track things. Looking something up by #dbref can be practical - if used occationally. It is however considered **bad practice** to *rely* on hard-coded #dbrefs in Evennia. Especially to expect end users to know them. It makes your code fragile and hard to maintain, while tying your code to the exact layout of the database. In 99% of use cases you should organize your code such that you pass the actual objects around and search by key/tags/attribute instead.
Let's consider a `chest` with a `coin` inside it. The chest stands in a room `dungeon`. In the dungeon is also a `door`. This is an exit leading outside.