auraxium.census
The auraxium.census
module provides a low-level Python wrapper around the PlanetSide 2 REST API. It is used internally by Auraxium to generate any URLs needed for traversal of the object model, but may also be used by advanced users to customize queries and increase performance of their apps.
The following pages cover the URL generator interface and usage, but the underlying REST endpoint's structure, functionality and limitations are outside the scope of this document. If you are unfamiliar with the Daybreak Game Company's Census API or would like a refresh, please refer to the Census API Primer in the repository Wiki.
For an example of how to use the URL generator and custom queries in conjunction with the Auraxium object model, refer to the ../advanced/queries
page.
Note
The URL generator is independent of the rest of the Auraxium package and may be imported separately. If your use case does not benefit from the object model or event streaming client, you can still use the URL generator on its own to keep your source code free of URL literals:
from auraxium import census
my_query = census.Query('character', name__first='Higby')
my_query.create_join('character_online_status')
print(type(my_query.url()))
# <class 'yarl.URL'>
print(my_query.url())
# http://census.daybreakgames.com/get/ps2/character?name.first=Higby&c:join=characters_online_status
auraxium.census
The auraxium.census
URL generator works by combining one or more queries and query commands into a single URL and query string. This can then be returned as a yarl.URL
or cast to str
.
Important
This module only generates URLs, it does not make requests and also does not provide any exception handling facilities. After generating your URL using the Query.url
factory, you must pass it to an HTTP library like requests
or aiohttp
to handle the web request.
Basic functionality supported by all queries is defined in the QueryBase
class. This includes specifying the collection to access, any number of key-value pairs to match (referred to as terms for the rest of the documentation), as well as the ~QueryBase.show
and ~QueryBase.hide
methods, which allow the exclusion of unneeded fields from the response.
Note
All query-like objects use a fluent interface for most of their configuration methods. This means that they all return their instance, which allows for method chaining:
from auraxium import census
# Without method chaining
my_query = census.Query('character', name__first='Auroram')
my_query.case(False)
my_query.show('name.first', 'prestige_level')
# With method chaining
my_query = (census.Query('character', name__first='Auroram')
.case(False).show('name.first', 'prestige_level'))
Use of this pattern is optional, but it can aid readability when working with complex or heavily nested queries.
This base class is mentioned here for completeness and due to its use in type hints and other parts of the documentation. However, most users will only ever need to directly interact with its subclasses, covered in the next sections.
For a full list of the basic query interface, refer to the QueryBase API reference<QueryBase>
below.
The PlanetSide 2 API defines two distinct types of queries. The first is the main query sent to the server, referred to as the top level query in this documentation. It is represented by the Query
class.
To generate a URL from a query, run the Query.url
method. By default, this uses the get
query verb. This is the main endpoint used to retrieve data and supports the entire query interface, and will return actual matches for a given query. Alternatively, one can specify verb='count'
to instead only return the number of potential matches. This is helpful when working with filtered data sets of unknown size, so that pagination or other custom formats can be used to display the requested data.
Top level queries also support global flags (or query commands) that allow for case insensitive<Query.case>
string comparison, sorting <Query.sort>
of results, or enable special query modes like ~Query.distinct
, illustrated below:
from auraxium import census
my_query = census.Query('map_region')
my_query.distinct('facility_type')
my_query.url()
# http://census.daybreakgames.com/get/ps2/map_region?c:distinct=facility_type
This query now won't return actual map regions, but instead up to 20'000 unique values for the given field:
{
"count": 7,
"map_region_list": [
{
"facility_type": [
"Amp Station",
"Bio Lab",
"Construction Outpost",
"Large Outpost",
"Small Outpost",
"Tech Plant",
"Warpgate"
]
}
],
"returned": 1
}
The other type of query are joined queries, also known as joins. These are represented by the JoinedQuery
class and behave much like regular queries, but are attached to an existing parent query, either a top level Query
or another join.
Joins are used to return data related to another query's return values as part of the same response. This allows effectively returning related data, such as the name of a player, their outfit, and their top played classes in a single request.
Important
Some API collections, like character
and outfit_member
, can have access times in excess of several seconds depending on your client's location. When working with these large tables, it is often preferable to use few large joins rather than multiple smaller queries to reduce latency.
In particular, it is often worth considering the use of single_character_by_id
, which is an indexed, high-performance table containing most data for a given character. The downside is that it contains all player statistics and items, which makes it a very bandwidth-intensive payload to transmit. It also does not support hiding fields or other query commands that could be used to reduce its size.
The relation between a join and its parent is defined by the ~JoinedQuery.set_fields
method. The joins data is then inserted into an extra field in the response, either named <parent_field>_join_<joined_collection>
(default) or a custom name specified through the JoinedQuery.set_inject_at
method.
Joined queries can not be directly translated into a URL, the must be attached to a top level query instead. This can be done via the QueryBase.add_join
(for existing joins) or QueryBase.create_join
(for new joins) methods. The JoinedQuery.serialise
method allows conversion of a JoinedQuery
into its URL-compatible, serialized format. This hook is used by the parent query when the top level query's Query.url
method is called.
For more information on joined queries, pitfalls and examples, refer to the Joined Queries section of the aforementioned Census API primer Wiki article.
The core features of a query (i.e. its collection, terms, show/hide field lists and any attached joins) are shared across all types of queries. This allows partial conversion between query types.
This conversion is done as part of the Query.copy
/JoinedQuery.copy
methods, which take in a template query and creates a new instance of their own class using any applicable values from the given template.
The key-value pairs used to filter queries (previously introduced as the query's terms) are represented by the SearchTerm
class, which provides facilities for parsing, serializing and formatting these terms for use in query strings.
Terms can be added directly using a query's QueryBase.add_term
method, or may alternatively be generated via keyword arguments as part of the query instantiation:
from auraxium import census
# Added manually
my_query = census.Query('character')
my_query.add_term('name.first', 'Higby')
# Added via keyword arguments
my_query = census.Query('character, name__first='Higby')
Note
When passing field names as keyword arguments, double underscores will be replaced with a single dot in the search term's ~SearchTerm.field
. This allows inline definition of nested keys as in the example above.
The QueryBase.add_term
method also provides a modifier field, which allows specifying the type of match to perform via the SearchModifier
enumerator. Search modifiers allow exact value matches (like SearchModifier.EQUAL_TO <SearchModifier>
or SearchModifier.NOT_EQUAL <SearchModifier>
), to ranges (SearchModifier.GREATER_THAN <SearchModifier>
, etc.), and even basic text value comparison (SearchModifier.STARTSWITH <SearchModifier>
and SearchModifier.CONTAINS <SearchModifier>
).
Note that it is allowed to pass the same value multiple times, with different constraints:
from auraxium import census
# Find players between BR 100 and 120
my_query = census.Query('character')
my_query.add_term('battle_rank.value', 100, census.SearchModifier.GREATER_THAN)
my_query.add_term('battle_rank.value', 120, census.SearchModifier.LESS_THAN_OR_EQUAL)
This example retrieves a character by name and joins their online status.
"""Usage example for the auraxium.census module."""
from auraxium import census
query = census.Query('character', service_id='s:example')
query.add_term('name.first_lower', 'auroram')
query.limit(20)
join = query.create_join('characters_online_status')
url = str(query.url())
print(url)
# https://census.daybreakgames.com/s:example/get/ps2:v2/character?c:limit=20&c:join=characters_online_status
QueryBase(collection=None, **kwargs)
__init__(collection: str | None = None, **kwargs: float | int | str) -> None
add_join(query: QueryBase, **kwargs) -> QueryBase
add_term(field: str, value: float | int | str, modifier: SearchModifier = SearchModifier.EQUAL_TO, *, parse_modifier: bool = False) -> QueryBase
copy(template: QueryBase, copy_joins: bool = False, deep_copy: bool = False, kwargs) -> QueryBase
create_join(collection: str, args,*kwargs) -> JoinedQuery
hide(field: str, *args: str) -> QueryBase
show(field: str, *args: str) -> QueryBase
Query(collection=None, namespace='ps2:v2', service_id='s:example', **kwargs)
__init__(collection: str | None = None, namespace: str = 'ps2:v2', service_id: str = 's:example', **kwargs: float | int | str) -> None
__str__() -> str
case(value: bool = True) -> Query
copy(template: QueryBase, copy_joins: bool = False, deep_copy: bool = False, **kwargs) -> Query
has(field: str, *args: str) -> Query
distinct(field: str | None) -> Query
exact_match_first(value: bool = True) -> Query
include_null(value: bool) -> Query
lang(lang: str | None = None) -> Query
limit(limit: int) -> Query
limit_per_db(limit_per_db: int) -> Query
offset(offset: int) -> Query
resolve(name: str, *args: str) -> Query
retry(retry: bool = False) -> Query
start(start: int) -> Query
sort(field: str | tuple[str, bool], *args: str | tuple[str, bool]) -> Query
timing(value: bool = True) -> Query
tree(field: str, is_list: bool = False, prefix: str = '', start: str | None = None) -> Query
url(verb: str = 'get', skip_checks: bool = False) -> yarl.URL
JoinedQuery(collection, **kwargs)
__init__(collection: str, **kwargs: float | int | str) -> None
copy(template: QueryBase, copy_joins: bool = False, deep_copy = False, **kwargs) -> JoinedQuery
serialise() -> JoinedQueryData
set_fields(parent: str | None, child: str | None = None) -> JoinedQuery
set_inject_at(key: str | None) -> JoinedQuery
set_list(is_list: bool) -> JoinedQuery
set_outer(is_outer: bool) -> JoinedQuery
SearchModifier()
from_value(value: float | int | str) -> SearchModifier
serialise(enum_value: int | SearchModifier) -> str
SearchTerm(field, value, modifier=SearchModifier.EQUAL_TO)
__init__(field: str, value: float | int | str, modifier: SearchModifier = SearchModifier.EQUAL_TO) -> None
as_tuple() -> tuple[str, str]
infer(field: str, value: float | int | str) -> SearchTerm
serialise() -> str
The following classes are mostly for internal use, but are provided for type inferrence and introspection.
QueryBaseData(**kwargs)
from_base(data: QueryBaseData) -> QueryBaseData
QueryData(**kwargs)
JoinedQueryData(**kwargs)