-
Notifications
You must be signed in to change notification settings - Fork 0
Expressive Vault Queries
Corda's vault query API implements the criteria pattern in which vault query criteria can be composed together using logical AND/OR operations, creating a criteria expression. That said, for new developers it will likely cause a few headaches before it begins to make sense. The following query criteria was taken directly from R3's documentation:
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))
vaultService.queryBy<Cash.State>(criteria)
}
It may not immediately be obvious what's going on here but essentially this query criteria is looking for:
- Contract states of type
Cash.State
-
currency
is equal to"USD"
-
pennies
is greater than or equal to10
That's a lot of code for a simple query, and as these queries grow, they become significantly harder to reason about. It's also another syntax that's unfamiliar to Kotlin developers who already have language-level query functions like filter
, single
, any
, count
, etc. Imagine if we could use functions like that in our vault queries...well, now you can!
There are several features in this library to make vault queries much more expressive and easier to maintain. The root of these features is the VaultService
which is an extensible wrapper over the ServiceHub
and CordaRPCOps
vault query implementations. On top of the vault service is a query DSL which makes query expressions really easy to write; that code above, when refactored to use the vault query service, looks like this:
val results = serviceHub.vaultServiceFor<Cash.State>().filter {
stateStatus(Vault.StateStatus.ALL)
expression(PersistentCashState::currency equalTo "USD")
expression(PersistentCashState::pennies greaterThanOrEqualTo 10L)
}
What's more result
in this case is a Sequence<T>
which is lazily evaluated; the sequence won't perform the query operation until its iterator is called.