Background
I have a problem with being able to query child data in a sample Corda application, demonstrating how to persisnt hierarchical data using QueryableState.
For reference:
- https://github.com/corda/samples-kotlin/tree/master/Features/queryableState-carinsurance
- https://www.corda.net/blog/persisting-corda-states-in-custom-database-tables-using-queryablestate/
The car insurance sample demonstrates how to set up:
- a One-to-One relationship between
PersistentInsuranceandPersistentVehicle - a One-to-Many relationship between
PersistentInsuranceandPersistentClaim
Problem
What the car insurance sample doesn't demonstrate is how to query that data. Building vault queries from PersistentInsurance is fairly trivial as we can use VaultCustomQueryCriteria to build custom query expressions over the properties of PersistentInsurance, however the same is not true for the child tables in the hierarchy. This is because PersistentInsurance extends PersistentState, whereas PersistentVehicle and PersistentClaim don't.
For Reference, the underlying type hierarchy for PersistentState is as follows:
StatePersistable > DirectStatePersistable > PersistentState
interface StatePersistable
interface DirectStatePersistable : StatePersistable {
val stateRef: PersistentStateRef?
}
class PersistentState(@EmbeddedId override var stateRef: PersistentStateRef? = null) : DirectStatePersistable
data class PersistentStateRef(
@Suppress("MagicNumber") // column width
@Column(name = "transaction_id", length = 144, nullable = false)
var txId: String,
@Column(name = "output_index", nullable = false)
var index: Int
) : Serializable {
constructor(stateRef: StateRef) : this(stateRef.txhash.toString(), stateRef.index)
}
Going back to the car insurance sample, we can't use VaultCustomQueryCriteria for PersistentVehicle or PersistentClaim as VaultCustomQueryCriteria::expression has a generic constraint on StatePersistable:
data class VaultCustomQueryCriteria<L : StatePersistable>(
val expression: CriteriaExpression<L, Boolean>,
...
) : CommonQueryCriteria() { ... }
What I've Tried
Implementing StatePersistable on PersistentVehicle and/or PersistentClaim causes the following error when you create a VaultCustomQueryCriteria using any of the properties from those classes:
net.corda.core.node.services.VaultQueryException: Parsing error: Unable to locate Attribute with the the given name [stateRef] on this ManagedType [...$PersistentClaim]
Implementing DirectStatePersistable or PersistentState on PersistentVehicle and/or PersistentClaim causes an internal error when finalizing the transaction. The flow hangs and eventually times out. I wasn't able to determine exactly where the error occurs as the code is buried inside internal implementations for underlying Corda services.
What Does Work (In a Limited Capacity)
I'm aware that we can execute custom SQL queries; for example:
val sqlQuery = buildString {
appendln("SELECT TRANSACTION_ID, OUTPUT_INDEX")
appendln("FROM CLAIM_DETAIL")
appendln("WHERE claimNumber = '123'")
}
val stateRefs = serviceHub.jdbcSession().executeCaseInsensitiveQuery(sqlQuery).map {
val txId = SecureHash.parse(it.getString("TRANSACTION_ID")
val index = it.getInt("OUTPUT_INDEX")
StateRef(txId, index)
}.toList().toBlocking().first()
VaultQueryCriteria(stateRefs = stateRefs)
Needless to say, that's hideous compared to:
VaultCustomQueryCriteria(PersistentClaim::claimNumber.equal("123"))
As for why it works in a limited capacity, this works fine for queries inside the node, which access to the service hub, but doesn't work over RPC as there's no access to a jdbcSession.
So, any ideas (if it's at all possible), on how we can improve the query capability of child tables?