corda/docs/source/vault-query.rst
josecoll 8c3b9ac589 Vault Query API design (#522)
* Added queryBy(QueryCriteria) Vault API and Junit tests.

* Minor fix following rebase.

* Spit out Vault Query tests into separate source file.

* WIP

* Enable composition of QueryCriteria specifications.
Additional JUnit test cases to validate API.

* Added Deprecating annotations.
Added QueryCriteria for set of contractStateTypes

* Minor tweaks and additional JUnit test cases (chain of linear id)

* Added Java Junit tests and QueryCriteria builder support.

* Added API documentation (including coding snippets and examples).

* Added @JvmOverloads to QueryCriteria classes for easy of use from Java.

* Refactored QueryCriteria API to use composition via sealed data classes.

* Enable infix notation.

* Fixed typo.

* Clarified future work to enforce DB level permissioning.

* Moved PageSpec and Order from QueryCriteria to become parameters of Query itself.

* Moved PageSpec and Order from QueryCriteria to become parameters of Query itself.

* TokenType now specified as set of <Class> (was non extensible enum).

* Exposed new Vault Query API functions via RPC.

* Fixed compiler error in java test.

* Addressed a couple of minor PR review scomments from MH.

* Major updates following PR discussion and recommendations.

* All pagination and sorting arguments are optional (and constructed with sensible defaults).
Added Java helper functions for queryBy and trackBy interfaces.
Added Java trackBy unit tests.
Miscellaneous cleanup.

* Added Generic Index schema mapping and query support.

* Query criteria referencing Party now references a String (until Identity framework built out).
Added participants attribute to general query criteria.

* Fleshed our IndexCriteria including PR recommendation to define column aliases for index mappings.

* Removed all directly exposed API dependencies on requery.

* Updated documentation.

* Provide sensible defaults for all Query arguments.
Add RPC Java helpers and increase range of Vault Service helpers.

* Further improvements (upgrading notes) and updates to documentation.

* RST documentation updates.

* Updates to address RP latest set of review comments.

* Updates to address MH latest set of review comments.

* Updated to highlight use of VaultIndexQueryCriteria to directly reference a JPA-annotated entity (versus the indirect, explicitly mapped attribute to GenericIndexSchema approach)

* Aesthetic updates requested by MH

* Reverted Indexing approach: removed all references to VaultIndexedQueryCriteria and GenericVaultIndexSchemaV1 scheme.

* Final clean-up and minor updates prior to merge.

* Fixed compiler warnings (except deprecation warnings)

* Reverted all changes to Vault Schemas (except simple illustrative VaultLinearState used in VaultQueryTests)

* Reverted all changes to Vault Schemas (except simple illustrative VaultLinearState used in VaultQueryTests)

* Commented out @Deprecated annotations (as a hedge against us releasing M12 with the work half-done)

* Renamed RPC JavaHelper functions as RPCDispatcher does not allow more than one method with same name.
2017-05-05 15:14:43 +01:00

15 KiB

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 satified by using the Vault Query API, which is exposed via the VaultService for use directly by flows:

../../core/src/main/kotlin/net/corda/core/node/services/Services.kt

and via CordaRPCOps for use by RPC client applications:

../../core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt

../../core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt

Java helper methods are also provided with default values for arguments:

../../core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt

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 given QueryCriteria)
  • Use trackBy to obtain a both a current snapshot and a future stream of updates (for a given QueryCriteria)

Simple pagination (page number and size) and sorting (directional ordering, null handling, custom property sort) is also specifiable. Defaults are defined for Paging (pageNumber = 0, pageSize = 200) and Sorting (direction = ASC, nullHandling = NULLS_LAST).

The QueryCriteria interface provides a flexible mechanism for specifying different filtering criteria, including and/or composition and a rich set of logical operators. There are four implementations of this interface which can be chained together to define advanced filters.

  1. 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).

  2. FungibleAssetQueryCriteria provides filterable criteria on attributes defined in the Corda Core FungibleAsset contract state interface, used to represent assets that are fungible, countable and issued by a specific party (eg. Cash.State and CommodityContract.State in the Corda finance module).

    Note

    Contract states that extend the FungibleAsset interface now automatically persist associated state attributes.

  3. LinearStateQueryCriteria provides filterable criteria on attributes defined in the Corda Core LinearState and DealState contract state interfaces, used to represent entities that continuously supercede themselves, all of which share the same linearId (eg. trade entity states such as the IRSState defined in the SIMM valuation demo)

    Note

    Contract states that extend the LinearState or DealState interfaces now automatically persist associated state attributes.

4. 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 as JPA Query like WHERE clauses as follows: [JPA entityAttributeName] [Operand] [Value]

An example is illustrated here:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

All QueryCriteria implementations are composable using and and or operators, as also illustrated above.

Additional notes

Note

Custom contract states that implement the Queryable interface may now extend the FungiblePersistentState, LinearPersistentState or DealPersistentState classes when implementing their MappedSchema. Previously, all custom contracts extended the root PersistentState class and defined repeated mappings of FungibleAsset, LinearState and DealState attributes.

Examples of these QueryCriteria objects are presented below for Kotlin and Java.

The Vault Query API leverages the rich semantics of the underlying Requery 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.

Note

Current queries by Party specify only a party name as the underlying identity framework is being re-designed. In future it may be possible to query on specific elements of a parties identity such as a CompositeKey hierarchy (parent and child nodes, weightings).

Example usage

Kotlin

General snapshot queries using VaultQueryCriteria

Query for all unconsumed states:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed states for some state references:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed states for several contract state types:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed states for a given notary:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed states for a given set of participants:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed states recorded between two time intervals:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for all states with pagination specification:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

LinearState and DealState queries using LinearStateQueryCriteria

Query for unconsumed linear states for given linear ids:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Note

This example was previously executed using the deprecated extension method:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for all linear states associated with a linear id:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Note

This example was previously executed using the deprecated method:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed deal states with deals references:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for unconsumed deal states with deals parties:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

FungibleAsset and DealState queries using FungibleAssetQueryCriteria

Query for fungible assets for a given currency:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for fungible assets for a given currency and minimum quantity:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for fungible assets for a specifc issuer party:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Query for consumed fungible assets with a specific exit key:

../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

Java examples

Query for all consumed contract states:

../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java

Note

This example was previously executed using the deprecated method:

../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java

Query for all deal states:

../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java

Note

This example was previously executed using the deprecated method:

../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java

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.

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:

  1. Template / Tutorial CorDapp service using Vault API Custom Query to access attributes of IOU State
  2. Template / Tutorial CorDapp service query extension executing Named Queries via JPQL
  3. Advanced pagination queries using Spring Data JPA

Upgrading from previous releases

Here follows a selection of the most common upgrade scenarios:

  1. 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>> sized according to the default Page specification of DEFAULT_PAGE_NUM (0) and DEFAULT_PAGE_SIZE (200).
  • states metadata as a List<Vault.StateMetadata> containing Vault State metadata held in the Vault states table.
  • the PagingSpecification used in the query
  • a total number of results available. This value can be used issue subsequent queries with appropriately specified PageSpecification (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.
  1. 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.")
  1. 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>()