Skip to content

Expressive Vault Queries

Matthew Layton edited this page Jun 22, 2021 · 1 revision

Introduction

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 to 10

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!

Vault Query API

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.

Clone this wiki locally