Querying Source Chains
An agent can query their source chain for a history of the records they’ve written, including link and public and private entry data, which includes capability grants and claims. They can also query the public portion of another agent’s source chain in a zome function or validate
callback.
An agent’s source chain is their record of local state changes. It’s a multi-purpose data structure, and can be interpreted in different ways, including as:
- a chronological record of contributions to the shared DHT,
- a ledger of changes to a single state,
- a store for private entry data, or
- a store for capability grants and claims.
Filtering a query
Before we talk about getting data, let’s talk about query filters, which apply to a few different sources of data.
Whether an agent is querying their own source chain or another agent’s, you build a query with the ChainQueryFilter
struct, which has a few filter types:
sequence_range: ChainQueryFilterRange
: A start and end point on the source chain, either:Unbounded
: the beginning and current tip of the chainActionSeqRange(u32, u32)
: start and end sequence indices, inclusive, zero-based.ActionHashRange(ActionHash, ActionHash)
: start and end action hashes, inclusive.ActionHashTerminated(ActionHash, u32)
: an action hash plus the _n_th actions preceding it.
entry_type: Option<Vec<EntryType>>
: Only select the given entry types, which can include both system and app entry types.entry_hashes: Option<HashSet<EntryHash>>
: Only select entry creation actions with the given entry hashes.action_type: Option<Vec<ActionType>>
: Only select actions of the given type.include_entries: bool
: Try to retrieve and include entry data for entry creation actions. Private entries will only be included forquery
, notget_agent_activity
.order_descending: bool
: Return the results in reverse chronological order, newest first and oldest last.
After retrieving the filtered records, you can then further filter them in memory using Rust’s standard Iterator
trait.
Use the builder interface
Rather than building a struct and having to specify fields you don’t need, you can use the builder interface on ChainQueryFilter
:
use hdk::prelude::*;
use movies::prelude::*;
let filter_only_movie_updates = ChainQueryFilter::new()
.entry_type(EntryType::App(UnitEntryTypes::Movie.into()))
.action_type(ActionType::Update)
.include_entries(true);
let filter_only_cap_grants_and_claims_newest_first = ChainQueryFilter::new()
.entry_type(EntryType::CapGrant)
.entry_type(EntryType::CapClaim)
.include_entries(true)
.descending();
let filter_first_ten_records = ChainQueryFilter::new()
.sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 9));
Use ChainQueryFilter
to query a vector of actions or records
If you already have a vector of Action
s or Records
in memory, you can apply a ChainQueryFilter
to them as if you were querying a source chain.
include hdk::prelude::*;
let actions: Vec<Action> = /* get some actions somehow */;
let movie_update_actions = filter_only_movie_updates.filter_actions(actions);
Query an agent’s own source chain
An agent can query their own source chain with the query
host function, which takes a ChainQueryFilter
and returns a Vec<Record>
wrapped in an ExternResult
.
use hdk::prelude::*;
use movies::prelude::*;
#[hdk_extern]
pub fn get_all_movies_i_authored() -> Vec<Record> {
query(ChainQueryFilter::new()
.entry_type(EntryType::App(UnitEntryTypes::Movie.into()))
.include_entries(true)
)
}
Query another agent’s source chain
In coordinator logic
In your coordinator zome functions, you can use the get_agent_activity
host function, which works a lot like query
if you pass ActivityRequest::Full
to it. The main differences are:
- It also returns the status of the chain (empty, valid, invalid, or forked) along with any collected warrants, and
- It returns action hashes but no action or entry data.
If you want to get the action data, you’ll need to perform a DHT query for every action hash you get back. The returned action hashes have been filtered by the query filters that you passed into the call.
This example gets the action hashes of all the movie entries authored by an arbitrary agent.
All DHT requests can fail
Because a DHT request often goes out to a remote node, it can fail to connect to that node, or they may not have the data you’re looking for. It’s up to you to build the retry logic that works best for your app. If you have program flow that requires multiple queries, it’s often a good practice to define zome functions as single-query functions that return DHT hashes so that the client can retrieve the rest of the data in follow-up zome calls. This way, work isn’t wasted if one query fails.
use hdk::prelude::*;
use movies::prelude::*;
#[hdk_extern]
pub fn get_hashes_of_all_movies_authored_by_agent(agent_id: AgentPubKey) -> ExternResult<Vec<ActionHash>> {
let activity = get_agent_activity(
agent_id,
ChainQueryFilter::new()
.entry_type(EntryType::App(UnitEntryTypes::Movie.into())),
// get_agent_activity ignores the include_entries filter, because
// the agent activity authorities don't store the entry data along
// with the actions.
ActivityRequest::Full
)?;
// The action hash is the second element in each tuple.
// (The first element is the sequence index.)
Ok(activity.valid_activity.into_iter().map(|(_, h)| h).collect())
}
During validation
There’s another source chain querying function called must_get_agent_activity
, which is used in validation to check whether a contiguous region of a source chain is valid. But you can also use it in a coordinator zome function to retrieve action data (not just the hashes), as long as you don’t need to filter on action or entry type. See the Validation page for an example.
Reference
holochain_zome_types::query::ChainQueryFilter
holochain_zome_types::query::ChainQueryFilterRange
holochain_integrity_types::action::EntryType
holochain_integrity_types::action::ActionType
hdk::chain::query
holochain_integrity_types::record::Record
hdk::chain::get_agent_activity
holochain_zome_types::query::ChainStatus
holochain_zome_types::query::AgentActivity
holochain_zome_types::query::ActivityRequest
hdi::chain::must_get_agent_activity
holochain_integrity_types::chain::ChainFilter