Chained Queries
Using Results to Scope a Second Query
Say you want sessions that have high-precision GPS data and contain error log messages. It's tempting to pass both conditions to a single client.query() call:
# Looks for ONE topic that is BOTH GPS data AND named /localization/log_string
client.query(
QueryOntologyCatalog(GPS.Q.status.status.eq(2)),
QueryTopic().with_name("/localization/log_string"),
)
This returns nothing. All builders passed to one client.query() call are ANDed together and evaluated against each individual topic — so the query above asks the daemon to find a topic that is simultaneously typed as GPS and named /localization/log_string. Since a topic has exactly one name and one ontology type, no topic can ever satisfy both, even if a GPS topic and a /localization/log_string topic both exist side by side in the very same sequence.
What you actually want is two separate topics — one carrying the GPS data, one carrying the logs — correlated within the same sequence. Chained queries solve this: run an initial broad query to find candidate sequences, convert the response into a new query builder scoped to exactly those sequences, then run a second query within that narrowed domain to find the log topic. You get the intersection of both conditions without loading any data client-side.
- Python
- C++
- Rust
The C++ SDK is currently in development.
The Rust SDK is currently in development.
How Chaining Works
Broad search. The first query casts a wide net. You may use only a QueryOntologyCatalog filter here, or combine it with a QuerySequence if you already know something about the sessions you care about. The result is a QueryResponse containing every sequence where the condition was satisfied.
Domain locking. Call to_query_sequence() on the response. This converts the QueryResponse into a new QuerySequence builder that is already pre-filtered to the sequence names returned by the first query. The second query will only search within those sessions; the daemon will not touch any other data. This is what makes chaining both correct and efficient: you are not re-scanning the entire catalog, just the sessions you already know are relevant.
Targeted refinement. Pass the locked QuerySequence as the first argument of a new client.query() call, then add whichever QueryTopic and QueryOntologyCatalog conditions you need. The daemon evaluates the combination and returns only sessions that satisfy every condition across both queries.
- Python
- C++
- Rust
from mosaicolabs import MosaicoClient, QueryTopic, QueryOntologyCatalog, GPS, String
with MosaicoClient.connect("localhost", 6726) as client:
# Step 1: find all sequences with high-precision GPS
initial_response = client.query(
QueryOntologyCatalog(GPS.Q.status.status.eq(2))
)
if initial_response:
# Step 2: lock the search domain to those sequences
refined_domain = initial_response.to_query_sequence()
# Step 3: within those sequences, find error log messages
# (wrap the tag in "*" wildcards to match it anywhere in the string;
# "[ERR]" alone would be parsed as a single-character set, not a substring)
final_results = client.query(
refined_domain,
QueryTopic().with_name("/localization/log_string"),
QueryOntologyCatalog(String.Q.data.match("*[ERR]*"))
)
if final_results:
for item in final_results:
print(f"Error found in: {item.sequence.name}")
The C++ SDK is currently in development.
The Rust SDK is currently in development.
Key Concepts
to_query_topic() is the topic-level equivalent of to_query_sequence() used above. Instead of locking down to a set of sequences, it produces a QueryTopic builder pre-scoped to the specific topic paths returned by the previous response. Use this when you want to chain on exact topic identity, for instance when the first query already narrowed you to a particular channel and you want the second query to be applied to that same channel without re-specifying its name.
Why not just filter client-side? Both queries in a chain run entirely server-side: the daemon evaluates the second filter against only the relevant data without transferring intermediate results over the network. For large catalogs, this is significantly faster than collecting sequence names from the first response and looping over them in Python.