API: Vault Query¶
Corda has been architected from the ground up to encourage usage of industry standard, proven query frameworks and libraries for accessing RDBMS backed transactional stores (including the Vault).
Corda provides a number of flexible query mechanisms for accessing the Vault:
- Vault Query API
- custom JPA/JPQL queries
- custom 3rd party Data Access frameworks such as Spring Data
The majority of query requirements can be satisfied by using the Vault Query API, which is exposed via the VaultQueryService
for use directly by flows:
/**
* Generic vault query function which takes a [QueryCriteria] object to define filters,
* optional [PageSpecification] and optional [Sort] modification criteria (default unsorted),
* and returns a [Vault.Page] object containing the following:
* 1. states as a List of <StateAndRef> (page number and size defined by [PageSpecification])
* 2. states metadata as a List of [Vault.StateMetadata] held in the Vault States table.
* 3. total number of results available if [PageSpecification] supplied (otherwise returns -1)
* 4. status types used in this query: UNCONSUMED, CONSUMED, ALL
* 5. other results (aggregate functions with/without using value groups)
*
* @throws VaultQueryException if the query cannot be executed for any reason
* (missing criteria or parsing error, paging errors, unsupported query, underlying database error)
*
* Notes
* If no [PageSpecification] is provided, a maximum of [DEFAULT_PAGE_SIZE] results will be returned.
* API users must specify a [PageSpecification] if they are expecting more than [DEFAULT_PAGE_SIZE] results,
* otherwise a [VaultQueryException] will be thrown alerting to this condition.
* It is the responsibility of the API user to request further pages and/or specify a more suitable [PageSpecification].
*/
@Throws(VaultQueryException::class)
fun <T : ContractState> _queryBy(criteria: QueryCriteria,
paging: PageSpecification,
sorting: Sort,
contractType: Class<out T>): Vault.Page<T>
/**
* Generic vault query function which takes a [QueryCriteria] object to define filters,
* optional [PageSpecification] and optional [Sort] modification criteria (default unsorted),
* and returns a [Vault.PageAndUpdates] object containing
* 1) a snapshot as a [Vault.Page] (described previously in [queryBy])
* 2) an [Observable] of [Vault.Update]
*
* @throws VaultQueryException if the query cannot be executed for any reason
*
* Notes: the snapshot part of the query adheres to the same behaviour as the [queryBy] function.
* the [QueryCriteria] applies to both snapshot and deltas (streaming updates).
*/
@Throws(VaultQueryException::class)
fun <T : ContractState> _trackBy(criteria: QueryCriteria,
paging: PageSpecification,
sorting: Sort,
contractType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update>
and via CordaRPCOps
for use by RPC client applications:
@RPCReturnsObservables
fun <T : ContractState> vaultQueryBy(criteria: QueryCriteria,
paging: PageSpecification,
sorting: Sort,
contractType: Class<out T>): Vault.Page<T>
@RPCReturnsObservables
fun <T : ContractState> vaultTrackBy(criteria: QueryCriteria,
paging: PageSpecification,
sorting: Sort,
contractType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update>
Helper methods are also provided with default values for arguments:
fun <T : ContractState> vaultQuery(contractType: Class<out T>): Vault.Page<T> {
return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractType)
}
fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractType: Class<out T>): Vault.Page<T> {
return vaultQueryBy(criteria, PageSpecification(), Sort(emptySet()), contractType)
}
fun <T : ContractState> vaultQueryByWithPagingSpec(contractType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> {
return vaultQueryBy(criteria, paging, Sort(emptySet()), contractType)
}
fun <T : ContractState> vaultQueryByWithSorting(contractType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
return vaultQueryBy(criteria, PageSpecification(), sorting, contractType)
}
fun <T : ContractState> vaultTrack(contractType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update> {
return vaultTrackBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractType)
}
fun <T : ContractState> vaultTrackByCriteria(contractType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update> {
return vaultTrackBy(criteria, PageSpecification(), Sort(emptySet()), contractType)
}
fun <T : ContractState> vaultTrackByWithPagingSpec(contractType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update> {
return vaultTrackBy(criteria, paging, Sort(emptySet()), contractType)
}
fun <T : ContractState> vaultTrackByWithSorting(contractType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update> {
return vaultTrackBy(criteria, PageSpecification(), sorting, contractType)
}
The API provides both static (snapshot) and dynamic (snapshot with streaming updates) methods for a defined set of filter criteria.
- Use
queryBy
to obtain a only current snapshot of data (for a givenQueryCriteria
) - Use
trackBy
to obtain a both a current snapshot and a future stream of updates (for a givenQueryCriteria
)
Note
Streaming updates are only filtered based on contract type and state status (UNCONSUMED, CONSUMED, ALL)
Simple pagination (page number and size) and sorting (directional ordering using standard or custom property attributes) is also specifiable. Defaults are defined for Paging (pageNumber = 1, pageSize = 200) and Sorting (direction = ASC).
The QueryCriteria
interface provides a flexible mechanism for specifying different filtering criteria, including and/or composition and a rich set of operators to include: binary logical (AND, OR), comparison (LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL), equality (EQUAL, NOT_EQUAL), likeness (LIKE, NOT_LIKE), nullability (IS_NULL, NOT_NULL), and collection based (IN, NOT_IN). Standard SQL-92 aggregate functions (SUM, AVG, MIN, MAX, COUNT) are also supported.
There are four implementations of this interface which can be chained together to define advanced filters.
VaultQueryCriteria
provides filterable criteria on attributes within the Vault states table: status (UNCONSUMED, CONSUMED), state reference(s), contract state type(s), notaries, soft locked states, timestamps (RECORDED, CONSUMED).Note
Sensible defaults are defined for frequently used attributes (status = UNCONSUMED, includeSoftlockedStates = true).
FungibleAssetQueryCriteria
provides filterable criteria on attributes defined in the Corda CoreFungibleAsset
contract state interface, used to represent assets that are fungible, countable and issued by a specific party (eg.Cash.State
andCommodityContract.State
in the Corda finance module). Filterable attributes include: participants(s), owner(s), quantity, issuer party(s) and issuer reference(s).Note
All contract states that extend the
FungibleAsset
now automatically persist that interfaces common state attributes to the vault_fungible_states table.LinearStateQueryCriteria
provides filterable criteria on attributes defined in the Corda CoreLinearState
andDealState
contract state interfaces, used to represent entities that continuously supercede themselves, all of which share the same linearId (eg. trade entity states such as theIRSState
defined in the SIMM valuation demo). Filterable attributes include: participant(s), linearId(s), dealRef(s).Note
All contract states that extend
LinearState
orDealState
now automatically persist those interfaces common state attributes to the vault_linear_states table.VaultCustomQueryCriteria
provides the means to specify one or many arbitrary expressions on attributes defined by a custom contract state that implements its own schema as described in the Persistence documentation and associated examples. Custom criteria expressions are expressed using one of several type-safeCriteriaExpression
: BinaryLogical, Not, ColumnPredicateExpression, AggregateFunctionExpression. TheColumnPredicateExpression
allows for specification arbitrary criteria using the previously enumerated operator types. TheAggregateFunctionExpression
allows for the specification of an aggregate function type (sum, avg, max, min, count) with optional grouping and sorting. Furthermore, a rich DSL is provided to enable simple construction of custom criteria using any combination ofColumnPredicate
. See theBuilder
object inQueryCriteriaUtils
for a complete specification of the DSL.Note
It is a requirement to register any custom contract schemas to be used in Vault Custom queries in the associated CordaPluginRegistry configuration for the respective CorDapp using the
requiredSchemas
configuration field (which specifies a set of MappedSchema)
An example of a custom query is illustrated here:
val generalCriteria = VaultQueryCriteria(Vault.StateStatus.ALL)
val results = builder {
val currencyIndex = PersistentCashState::currency.equal(USD.currencyCode)
val quantityIndex = PersistentCashState::pennies.greaterThanOrEqual(10L)
val customCriteria1 = VaultCustomQueryCriteria(currencyIndex)
val customCriteria2 = VaultCustomQueryCriteria(quantityIndex)
val criteria = generalCriteria.and(customCriteria1.and(customCriteria2))
vaultQuerySvc.queryBy<Cash.State>(criteria)
}
All QueryCriteria
implementations are composable using and
and or
operators, as also illustrated above.
All QueryCriteria
implementations provide an explicitly specifiable StateStatus
attribute which defaults to filtering on UNCONSUMED states.
Note
Custom contract states that implement the Queryable
interface may now extend common schemas types FungiblePersistentState
or, LinearPersistentState
. Previously, all custom contracts extended the root PersistentState
class and defined repeated mappings of FungibleAsset
and LinearState
attributes. See SampleCashSchemaV2
and DummyLinearStateSchemaV2
as examples.
Examples of these QueryCriteria
objects are presented below for Kotlin and Java.
Note
When specifying the Contract Type as a parameterised type to the QueryCriteria in Kotlin, queries now include all concrete implementations of that type if this is an interface. Previously, it was only possible to query on Concrete types (or the universe of all Contract States).
The Vault Query API leverages the rich semantics of the underlying JPA Hibernate based Persistence framework adopted by Corda.
Note
Permissioning at the database level will be enforced at a later date to ensure authenticated, role-based, read-only access to underlying Corda tables.
Note
API’s now provide ease of use calling semantics from both Java and Kotlin. However, it should be noted that Java custom queries are significantly more verbose due to the use of reflection fields to reference schema attribute types.
An example of a custom query in Java is illustrated here:
QueryCriteria generalCriteria = new VaultQueryCriteria(Vault.StateStatus.ALL);
Field attributeCurrency = CashSchemaV1.PersistentCashState.class.getDeclaredField("currency");
Field attributeQuantity = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
CriteriaExpression currencyIndex = Builder.equal(attributeCurrency, "USD");
CriteriaExpression quantityIndex = Builder.greaterThanOrEqual(attributeQuantity, 10L);
QueryCriteria customCriteria2 = new VaultCustomQueryCriteria(quantityIndex);
QueryCriteria customCriteria1 = new VaultCustomQueryCriteria(currencyIndex);
QueryCriteria criteria = generalCriteria.and(customCriteria1).and(customCriteria2);
Vault.Page<ContractState> results = vaultQuerySvc.queryBy(Cash.State.class, criteria);
Note
Current queries by Party
specify the AbstractParty
which may be concrete or anonymous. In the later case, where an anonymous party does not have an associated X500Name, then no query results will ever be produced. For performance reasons, queries do not use PublicKey as search criteria. Ongoing design work on identity manangement is likely to enhance identity based queries (including composite key criteria selection).
Pagination¶
The API provides support for paging where large numbers of results are expected (by default, a page size is set to 200 results).
Defining a sensible default page size enables efficient checkpointing within flows, and frees the developer from worrying about pagination where
result sets are expected to be constrained to 200 or fewer entries. Where large result sets are expected (such as using the RPC API for reporting and/or UI display), it is strongly recommended to define a PageSpecification
to correctly process results with efficient memory utilistion. A fail-fast mode is in place to alert API users to the need for pagination where a single query returns more than 200 results and no PageSpecification
has been supplied.
Note
A pages maximum size MAX_PAGE_SIZE
is defined as Int.MAX_VALUE
and should be used with extreme caution as results returned may exceed your JVM’s memory footprint.
Example usage¶
Kotlin¶
General snapshot queries using VaultQueryCriteria
Query for all unconsumed states (simplest query possible):
val result = vaultQuerySvc.queryBy<ContractState>()
/**
* Query result returns a [Vault.Page] which contains:
* 1) actual states as a list of [StateAndRef]
* 2) state reference and associated vault metadata as a list of [Vault.StateMetadata]
* 3) [PageSpecification] used to delimit the size of items returned in the result set (defaults to [DEFAULT_PAGE_SIZE])
* 4) Total number of items available (to aid further pagination if required)
*/
val states = result.states
val metadata = result.statesMetadata
Query for unconsumed states for some state references:
val sortAttribute = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID)
val criteria = VaultQueryCriteria(stateRefs = listOf(stateRefs.first(), stateRefs.last()))
val results = vaultQuerySvc.queryBy<DummyLinearContract.State>(criteria, Sort(setOf(Sort.SortColumn(sortAttribute, Sort.Direction.ASC))))
Query for unconsumed states for several contract state types:
val criteria = VaultQueryCriteria(contractStateTypes = setOf(Cash.State::class.java, DealState::class.java))
val results = vaultQuerySvc.queryBy<ContractState>(criteria)
Query for unconsumed states for a given notary:
val criteria = VaultQueryCriteria(notaryName = listOf(CASH_NOTARY.name))
val results = vaultQuerySvc.queryBy<ContractState>(criteria)
Note
We are using the notaries X500Name as our search identifier.
Query for unconsumed states for a given set of participants:
val criteria = LinearStateQueryCriteria(participants = listOf(MEGA_CORP, MINI_CORP))
val results = vaultQuerySvc.queryBy<ContractState>(criteria)
Query for unconsumed states recorded between two time intervals:
val start = TODAY
val end = TODAY.plus(30, ChronoUnit.DAYS)
val recordedBetweenExpression = TimeCondition(
QueryCriteria.TimeInstantType.RECORDED,
ColumnPredicate.Between(start, end))
val criteria = VaultQueryCriteria(timeCondition = recordedBetweenExpression)
val results = vaultQuerySvc.queryBy<ContractState>(criteria)
Note
This example illustrates usage of a Between ColumnPredicate.
Query for all states with pagination specification (10 results per page):
val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, 10)
val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
val results = vaultQuerySvc.queryBy<ContractState>(criteria, paging = pagingSpec)
Note
The result set metadata field totalStatesAvailable allows you to further paginate accordingly.
LinearState and DealState queries using LinearStateQueryCriteria
Query for unconsumed linear states for given linear ids:
val linearIds = issuedStates.states.map { it.state.data.linearId }.toList()
val criteria = LinearStateQueryCriteria(linearId = listOf(linearIds.first(), linearIds.last()))
val results = vaultQuerySvc.queryBy<LinearState>(criteria)
This example was previously executed using the deprecated extension method:
val states = vaultSvc.linearHeadsOfType<DummyLinearContract.State>().filter { it.key == linearId }
Query for all linear states associated with a linear id:
val linearStateCriteria = LinearStateQueryCriteria(linearId = listOf(linearId), status = Vault.StateStatus.ALL)
val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
val results = vaultQuerySvc.queryBy<LinearState>(linearStateCriteria and vaultCriteria)
This example was previously executed using the deprecated method:
val states = vaultSvc.consumedStates<DummyLinearContract.State>().filter { it.state.data.linearId == linearId }
Query for unconsumed deal states with deals references:
val criteria = LinearStateQueryCriteria(dealRef = listOf("456", "789"))
val results = vaultQuerySvc.queryBy<DealState>(criteria)
Query for unconsumed deal states with deals parties:
val criteria = LinearStateQueryCriteria(participants = parties)
val results = vaultQuerySvc.queryBy<DealState>(criteria)
FungibleAsset and DealState queries using FungibleAssetQueryCriteria
Query for fungible assets for a given currency:
val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(USD.currencyCode) }
val criteria = VaultCustomQueryCriteria(ccyIndex)
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(criteria)
Query for fungible assets for a minimum quantity:
val fungibleAssetCriteria = FungibleAssetQueryCriteria(quantity = builder { greaterThan(2500L) })
val results = vaultQuerySvc.queryBy<Cash.State>(fungibleAssetCriteria)
Note
This example uses the builder DSL.
Query for fungible assets for a specifc issuer party:
val criteria = FungibleAssetQueryCriteria(issuerPartyName = listOf(BOC))
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(criteria)
Aggregate Function queries using VaultCustomQueryCriteria
Note
Query results for aggregate functions are contained in the otherResults attribute of a results Page.
Aggregations on cash using various functions:
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum() }
val sumCriteria = VaultCustomQueryCriteria(sum)
val count = builder { CashSchemaV1.PersistentCashState::pennies.count() }
val countCriteria = VaultCustomQueryCriteria(count)
val max = builder { CashSchemaV1.PersistentCashState::pennies.max() }
val maxCriteria = VaultCustomQueryCriteria(max)
val min = builder { CashSchemaV1.PersistentCashState::pennies.min() }
val minCriteria = VaultCustomQueryCriteria(min)
val avg = builder { CashSchemaV1.PersistentCashState::pennies.avg() }
val avgCriteria = VaultCustomQueryCriteria(avg)
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(sumCriteria
.and(countCriteria)
.and(maxCriteria)
.and(minCriteria)
.and(avgCriteria))
Note
otherResults will contain 5 items, one per calculated aggregate function.
Aggregations on cash grouped by currency for various functions:
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
val sumCriteria = VaultCustomQueryCriteria(sum)
val max = builder { CashSchemaV1.PersistentCashState::pennies.max(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
val maxCriteria = VaultCustomQueryCriteria(max)
val min = builder { CashSchemaV1.PersistentCashState::pennies.min(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
val minCriteria = VaultCustomQueryCriteria(min)
val avg = builder { CashSchemaV1.PersistentCashState::pennies.avg(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
val avgCriteria = VaultCustomQueryCriteria(avg)
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(sumCriteria
.and(maxCriteria)
.and(minCriteria)
.and(avgCriteria))
Note
otherResults will contain 24 items, one result per calculated aggregate function per currency (the grouping attribute - currency in this case - is returned per aggregate result).
Sum aggregation on cash grouped by issuer party and currency and sorted by sum:
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::issuerParty,
CashSchemaV1.PersistentCashState::currency),
orderBy = Sort.Direction.DESC)
}
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(VaultCustomQueryCriteria(sum))
Note
otherResults will contain 12 items sorted from largest summed cash amount to smallest, one result per calculated aggregate function per issuer party and currency (grouping attributes are returned per aggregate result).
Dynamic queries (also using VaultQueryCriteria
) are an extension to the snapshot queries by returning an additional QueryResults
return type in the form of an Observable<Vault.Update>
. Refer to ReactiveX Observable for a detailed understanding and usage of this type.
Track unconsumed cash states:
val (snapshot, updates) = vaultQuerySvc.trackBy<Cash.State>() // UNCONSUMED default
Track unconsumed linear states:
val (snapshot, updates) = vaultQuerySvc.trackBy<LinearState>()
Note
This will return both Deal and Linear states.
Track unconsumed deal states:
val (snapshot, updates) = vaultQuerySvc.trackBy<DealState>()
Note
This will return only Deal states.
Java examples¶
Query for all unconsumed linear states:
Vault.Page<LinearState> results = vaultQuerySvc.queryBy(LinearState.class);
This example was previously executed using the deprecated method:
@SuppressWarnings("unchecked")
Set<Class<LinearState>> contractStateTypes = new HashSet(Collections.singletonList(DummyLinearContract.State.class));
EnumSet<Vault.StateStatus> status = EnumSet.of(Vault.StateStatus.CONSUMED);
// WARNING! unfortunately cannot use inlined reified Kotlin extension methods.
Iterable<StateAndRef<LinearState>> results = vaultSvc.states(contractStateTypes, status, true);
Query for all consumed cash states:
VaultQueryCriteria criteria = new VaultQueryCriteria(Vault.StateStatus.CONSUMED);
Vault.Page<Cash.State> results = vaultQuerySvc.queryBy(Cash.State.class, criteria);
This example was previously executed using the deprecated method:
@SuppressWarnings("unchecked")
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
EnumSet<Vault.StateStatus> status = EnumSet.of(Vault.StateStatus.CONSUMED);
// WARNING! unfortunately cannot use inlined reified Kotlin extension methods.
Iterable<StateAndRef<ContractState>> results = vaultSvc.states(contractStateTypes, status, true);
Query for consumed deal states or linear ids, specify a paging specification and sort by unique identifier:
Vault.StateStatus status = Vault.StateStatus.CONSUMED;
@SuppressWarnings("unchecked")
Set<Class<LinearState>> contractStateTypes = new HashSet(Collections.singletonList(LinearState.class));
QueryCriteria vaultCriteria = new VaultQueryCriteria(status, contractStateTypes);
List<UniqueIdentifier> linearIds = Collections.singletonList(uid);
QueryCriteria linearCriteriaAll = new LinearStateQueryCriteria(null, linearIds);
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(null, null, dealIds);
QueryCriteria compositeCriteria1 = dealCriteriaAll.or(linearCriteriaAll);
QueryCriteria compositeCriteria2 = vaultCriteria.and(compositeCriteria1);
PageSpecification pageSpec = new PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE);
Sort.SortColumn sortByUid = new Sort.SortColumn(new SortAttribute.Standard(Sort.LinearStateAttribute.UUID), Sort.Direction.DESC);
Sort sorting = new Sort(ImmutableSet.of(sortByUid));
Vault.Page<LinearState> results = vaultQuerySvc.queryBy(LinearState.class, compositeCriteria2, pageSpec, sorting);
Aggregate Function queries using VaultCustomQueryCriteria
Aggregations on cash using various functions:
Aggregations on cash grouped by currency for various functions:
Sum aggregation on cash grouped by issuer party and currency and sorted by sum:
Track unconsumed cash states:
@SuppressWarnings("unchecked")
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
VaultQueryCriteria criteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED, contractStateTypes);
DataFeed<Vault.Page<ContractState>, Vault.Update> results = vaultQuerySvc.trackBy(ContractState.class, criteria);
Vault.Page<ContractState> snapshot = results.getSnapshot();
Observable<Vault.Update> updates = results.getUpdates();
Track unconsumed deal states or linear states (with snapshot including specification of paging and sorting by unique identifier):
@SuppressWarnings("unchecked")
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
VaultQueryCriteria criteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED, contractStateTypes);
DataFeed<Vault.Page<ContractState>, Vault.Update> results = vaultQuerySvc.trackBy(ContractState.class, criteria);
Vault.Page<ContractState> snapshot = results.getSnapshot();
Observable<Vault.Update> updates = results.getUpdates();
Behavioural notes¶
- TrackBy updates do not take into account the full criteria specification due to different and more restrictive syntax
in observables filtering (vs full SQL-92 JDBC filtering as used in snapshot views).
Specifically, dynamic updates are filtered by
contractType
andstateType
(UNCONSUMED, CONSUMED, ALL) only. - QueryBy and TrackBy snapshot views using pagination may return different result sets as each paging request is a
separate SQL query on the underlying database, and it is entirely conceivable that state modifications are taking
place in between and/or in parallel to paging requests.
When using pagination, always check the value of the
totalStatesAvailable
(from theVault.Page
result) and adjust further paging requests appropriately.
Other use case scenarios¶
For advanced use cases that require sophisticated pagination, sorting, grouping, and aggregation functions, it is recommended that the CorDapp developer utilise one of the many proven frameworks that ship with this capability out of the box. Namely, implementations of JPQL (JPA Query Language) such as Hibernate for advanced SQL access, and Spring Data for advanced pagination and ordering constructs.
The Corda Tutorials provide examples satisfying these additional Use Cases:
- Template / Tutorial CorDapp service using Vault API Custom Query to access attributes of IOU State
- Template / Tutorial CorDapp service query extension executing Named Queries via JPQL
- Advanced pagination queries using Spring Data JPA
Upgrading from previous releases¶
Here follows a selection of the most common upgrade scenarios:
ServiceHub usage to obtain Unconsumed states for a given contract state type
Previously:
val yoStates = b.vault.unconsumedStates<Yo.State>()
This query returned an Iterable<StateAndRef<T>>
Now:
val yoStates = b.vault.queryBy<Yo.State>().states
The query returns a Vault.Page
result containing:
- states as a
List<StateAndRef<T : ContractState>>
up to a maximum ofDEFAULT_PAGE_SIZE
(200) where noPageSpecification
provided, otherwise returns results according to the parameterspageNumber
andpageSize
specified in the suppliedPageSpecification
.- states metadata as a
List<Vault.StateMetadata>
containing Vault State metadata held in the Vault states table.- a
total
number of results available ifPageSpecification
provided (otherwise returns -1). For pagination, this value can be used to issue subsequent queries with appropriately specifiedPageSpecification
parameters (according to your paging needs and/or maximum memory capacity for holding large data sets). Note it is your responsibility to manage page numbers and sizes.- status types used in this query: UNCONSUMED, CONSUMED, ALL
- other results as a [List] of any type (eg. aggregate function results with/without group by)
ServiceHub usage obtaining linear heads for a given contract state type
Previously:
val iouStates = serviceHub.vaultService.linearHeadsOfType<IOUState>()
val iouToSettle = iouStates[linearId] ?: throw Exception("IOUState with linearId $linearId not found.")
Now:
val criteria = QueryCriteria.LinearStateQueryCriteria(linearId = listOf(linearId))
val iouStates = serviceHub.vaultService.queryBy<IOUState>(criteria).states
val iouToSettle = iouStates.singleOrNull() ?: throw Exception("IOUState with linearId $linearId not found.")
RPC usage was limited to using the
vaultAndUpdates
RPC method, which returned a snapshot and streaming updates as an Observable. In many cases, queries were not interested in the streaming updates.Previously:
val iouStates = services.vaultAndUpdates().first.filter { it.state.data is IOUState }
Now:
val iouStates = services.vaultQueryBy<IOUState>()