Vault query criteria default attribute handling. (#1322)

* Performance fix: prevent self joins on VaultStates table (was occurring when Sort specified).

* Enrichment and overriding of Common attributes (eg. Vault.StateStatus and Contract State Types) using composite query criteria.
Remove unnecessary QueryEditor implementation from NodeVaultService.

* Updated documentation and changelog.

* Misc fixes to broken documentation code snippets.

* Incorporating changes from PR review feedback.
This commit is contained in:
josecoll 2017-08-25 08:38:12 +01:00 committed by GitHub
parent 1750ab07af
commit a3dbbc173b
8 changed files with 129 additions and 144 deletions

View File

@ -40,6 +40,7 @@ sealed class QueryCriteria {
abstract class CommonQueryCriteria : QueryCriteria() {
abstract val status: Vault.StateStatus
abstract val contractStateTypes: Set<Class<out ContractState>>?
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this)
}
@ -49,13 +50,14 @@ sealed class QueryCriteria {
* VaultQueryCriteria: provides query by attributes defined in [VaultSchema.VaultStates]
*/
data class VaultQueryCriteria @JvmOverloads constructor (override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
val contractStateTypes: Set<Class<out ContractState>>? = null,
override val contractStateTypes: Set<Class<out ContractState>>? = null,
val stateRefs: List<StateRef>? = null,
val notary: List<AbstractParty>? = null,
val softLockingCondition: SoftLockingCondition? = null,
val timeCondition: TimeCondition? = null) : CommonQueryCriteria() {
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this as CommonQueryCriteria).plus(parser.parseCriteria(this))
super.visit(parser)
return parser.parseCriteria(this)
}
}
@ -65,9 +67,11 @@ sealed class QueryCriteria {
data class LinearStateQueryCriteria @JvmOverloads constructor(val participants: List<AbstractParty>? = null,
val uuid: List<UUID>? = null,
val externalId: List<String>? = null,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED) : CommonQueryCriteria() {
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this as CommonQueryCriteria).plus(parser.parseCriteria(this))
super.visit(parser)
return parser.parseCriteria(this)
}
}
@ -83,9 +87,11 @@ sealed class QueryCriteria {
val quantity: ColumnPredicate<Long>? = null,
val issuer: List<AbstractParty>? = null,
val issuerRef: List<OpaqueBytes>? = null,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED) : CommonQueryCriteria() {
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this as CommonQueryCriteria).plus(parser.parseCriteria(this))
super.visit(parser)
return parser.parseCriteria(this)
}
}
@ -101,9 +107,11 @@ sealed class QueryCriteria {
*/
data class VaultCustomQueryCriteria<L : PersistentState> @JvmOverloads constructor
(val expression: CriteriaExpression<L, Boolean>,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED) : CommonQueryCriteria() {
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this as CommonQueryCriteria).plus(parser.parseCriteria(this))
super.visit(parser)
return parser.parseCriteria(this)
}
}

View File

@ -9,39 +9,36 @@ import kotlin.reflect.KProperty1
import kotlin.reflect.jvm.javaGetter
@CordaSerializable
enum class BinaryLogicalOperator {
interface Operator
enum class BinaryLogicalOperator : Operator {
AND,
OR
}
@CordaSerializable
enum class EqualityComparisonOperator {
enum class EqualityComparisonOperator : Operator {
EQUAL,
NOT_EQUAL
}
@CordaSerializable
enum class BinaryComparisonOperator {
enum class BinaryComparisonOperator : Operator {
LESS_THAN,
LESS_THAN_OR_EQUAL,
GREATER_THAN,
GREATER_THAN_OR_EQUAL,
}
@CordaSerializable
enum class NullOperator {
enum class NullOperator : Operator {
IS_NULL,
NOT_NULL
}
@CordaSerializable
enum class LikenessOperator {
enum class LikenessOperator : Operator {
LIKE,
NOT_LIKE
}
@CordaSerializable
enum class CollectionOperator {
enum class CollectionOperator : Operator {
IN,
NOT_IN
}

View File

@ -70,6 +70,15 @@ There are four implementations of this interface which can be chained together t
.. 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`)
All ``QueryCriteria`` implementations are composable using ``and`` and ``or`` operators, as also illustrated above.
All ``QueryCriteria`` implementations provide an explicitly specifiable set of common attributes:
1. State status attribute (``Vault.StateStatus``), which defaults to filtering on UNCONSUMED states.
When chaining several criterias using AND / OR, the last value of this attribute will override any previous.
2. Contract state types (``<Set<Class<out ContractState>>``), which will contain at minimum one type (by default this will be ``ContractState`` which resolves to all types).
When chaining several criteria using AND / OR, all specified contract state types are combined into a single set.
An example of a custom query is illustrated here:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
@ -77,10 +86,6 @@ An example of a custom query is illustrated here:
:start-after: DOCSTART VaultQueryExample20
:end-before: DOCEND VaultQueryExample20
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.
@ -317,22 +322,22 @@ Query for consumed deal states or linear ids, specify a paging specification and
Aggregations on cash using various functions:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJavaTests.kt
:language: kotlin
.. literalinclude:: ../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
:language: java
:start-after: DOCSTART VaultJavaQueryExample21
:end-before: DOCEND VaultJavaQueryExample21
Aggregations on cash grouped by currency for various functions:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJavaTests.kt
:language: kotlin
.. literalinclude:: ../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
:language: java
:start-after: DOCSTART VaultJavaQueryExample22
:end-before: DOCEND VaultJavaQueryExample22
Sum aggregation on cash grouped by issuer party and currency and sorted by sum:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJavaTests.kt
:language: kotlin
.. literalinclude:: ../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
:language: java
:start-after: DOCSTART VaultJavaQueryExample23
:end-before: DOCEND VaultJavaQueryExample23

View File

@ -6,6 +6,9 @@ from the previous milestone release.
UNRELEASED
----------
* Vault query common attributes (state status and contract state types) are now handled correctly when using composite
criteria specifications. State status is overridable. Contract states types are aggregatable.
* Cash selection algorithm is now pluggable (with H2 being the default implementation)
* Removed usage of Requery ORM library (repalced with JPA/Hibernate)

View File

@ -13,6 +13,9 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.toHexString
import net.corda.core.utilities.trace
import org.hibernate.query.criteria.internal.expression.LiteralExpression
import org.hibernate.query.criteria.internal.predicate.ComparisonPredicate
import org.hibernate.query.criteria.internal.predicate.InPredicate
import java.util.*
import javax.persistence.Tuple
import javax.persistence.criteria.*
@ -30,8 +33,9 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
// incrementally build list of join predicates
private val joinPredicates = mutableListOf<Predicate>()
// incrementally build list of root entities (for later use in Sort parsing)
private val rootEntities = mutableMapOf<Class<out PersistentState>, Root<*>>()
private val rootEntities = mutableMapOf<Class<out PersistentState>, Root<*>>(Pair(VaultSchemaV1.VaultStates::class.java, vaultStates))
private val aggregateExpressions = mutableListOf<Expression<*>>()
private val commonPredicates = mutableMapOf<Pair<String,Operator>, Predicate>() // schema attribute Name, operator -> predicate
var stateTypes: Vault.StateStatus = Vault.StateStatus.UNCONSUMED
@ -39,11 +43,6 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
log.trace { "Parsing VaultQueryCriteria: $criteria" }
val predicateSet = mutableSetOf<Predicate>()
// contract State Types
val contractTypes = deriveContractTypes(criteria.contractStateTypes)
if (contractTypes.isNotEmpty())
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("contractStateClassName").`in`(contractTypes)))
// soft locking
criteria.softLockingCondition?.let {
val softLocking = criteria.softLockingCondition
@ -91,12 +90,14 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
return predicateSet
}
private fun deriveContractTypes(contractStateTypes: Set<Class<out ContractState>>? = null): List<String> {
private fun deriveContractTypes(contractStateTypes: Set<Class<out ContractState>>? = null): Set<String> {
log.trace { "Contract types to be derived: primary ($contractType), additional ($contractStateTypes)" }
val combinedContractStateTypes = contractStateTypes?.plus(contractType) ?: setOf(contractType)
combinedContractStateTypes.filter { it.name != ContractState::class.java.name }.let {
val interfaces = it.flatMap { contractTypeMappings[it.name] ?: listOf(it.name) }
val interfaces = it.flatMap { contractTypeMappings[it.name] ?: setOf(it.name) }
val concrete = it.filter { !it.isInterface }.map { it.name }
return interfaces.plus(concrete)
log.trace { "Derived contract types: ${interfaces.union(concrete)}" }
return interfaces.union(concrete)
}
}
@ -233,11 +234,6 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
val joinPredicate = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), vaultFungibleStates.get<PersistentStateRef>("stateRef"))
predicateSet.add(joinPredicate)
// contract State Types
val contractTypes = deriveContractTypes()
if (contractTypes.isNotEmpty())
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("contractStateClassName").`in`(contractTypes)))
// owner
criteria.owner?.let {
val owners = criteria.owner as List<AbstractParty>
@ -282,11 +278,6 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
val joinPredicate = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), vaultLinearStates.get<PersistentStateRef>("stateRef"))
joinPredicates.add(joinPredicate)
// contract State Types
val contractTypes = deriveContractTypes()
if (contractTypes.isNotEmpty())
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("contractStateClassName").`in`(contractTypes)))
// linear ids UUID
criteria.uuid?.let {
val uuids = criteria.uuid as List<UUID>
@ -322,11 +313,6 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
val joinPredicate = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<PersistentStateRef>("stateRef"))
joinPredicates.add(joinPredicate)
// contract State Types
val contractTypes = deriveContractTypes()
if (contractTypes.isNotEmpty())
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("contractStateClassName").`in`(contractTypes)))
// resolve general criteria expressions
parseExpression(entityRoot, criteria.expression, predicateSet)
}
@ -379,11 +365,11 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
val selections =
if (aggregateExpressions.isEmpty())
listOf(vaultStates).plus(rootEntities.map { it.value })
rootEntities.map { it.value }
else
aggregateExpressions
criteriaQuery.multiselect(selections)
val combinedPredicates = joinPredicates.plus(predicateSet)
val combinedPredicates = joinPredicates.plus(predicateSet).plus(commonPredicates.values)
criteriaQuery.where(*combinedPredicates.toTypedArray())
return predicateSet
@ -391,14 +377,39 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
override fun parseCriteria(criteria: CommonQueryCriteria): Collection<Predicate> {
log.trace { "Parsing CommonQueryCriteria: $criteria" }
val predicateSet = mutableSetOf<Predicate>()
// state status
stateTypes = criteria.status
if (criteria.status != Vault.StateStatus.ALL)
predicateSet.add(criteriaBuilder.equal(vaultStates.get<Vault.StateStatus>("stateStatus"), criteria.status))
if (criteria.status != Vault.StateStatus.ALL) {
val predicateID = Pair(VaultSchemaV1.VaultStates::stateStatus.name, EqualityComparisonOperator.EQUAL)
if (commonPredicates.containsKey(predicateID)) {
val existingStatus = ((commonPredicates[predicateID] as ComparisonPredicate).rightHandOperand as LiteralExpression).literal
if (existingStatus != criteria.status) {
log.warn("Overriding previous attribute [${VaultSchemaV1.VaultStates::stateStatus.name}] value $existingStatus with ${criteria.status}")
commonPredicates.replace(predicateID, criteriaBuilder.equal(vaultStates.get<Vault.StateStatus>(VaultSchemaV1.VaultStates::stateStatus.name), criteria.status))
}
}
else {
commonPredicates.put(predicateID, criteriaBuilder.equal(vaultStates.get<Vault.StateStatus>(VaultSchemaV1.VaultStates::stateStatus.name), criteria.status))
}
}
return predicateSet
// contract state types
val contractTypes = deriveContractTypes(criteria.contractStateTypes)
if (contractTypes.isNotEmpty()) {
val predicateID = Pair(VaultSchemaV1.VaultStates::contractStateClassName.name, CollectionOperator.IN)
if (commonPredicates.containsKey(predicateID)) {
val existingTypes = (commonPredicates[predicateID]!!.expressions[0] as InPredicate<*>).values.map { (it as LiteralExpression).literal }.toSet()
if (existingTypes != contractTypes) {
log.warn("Enriching previous attribute [${VaultSchemaV1.VaultStates::contractStateClassName.name}] values [$existingTypes] with [$contractTypes]")
commonPredicates.replace(predicateID, criteriaBuilder.and(vaultStates.get<String>(VaultSchemaV1.VaultStates::contractStateClassName.name).`in`(contractTypes.plus(existingTypes))))
}
} else {
commonPredicates.put(predicateID, criteriaBuilder.and(vaultStates.get<String>(VaultSchemaV1.VaultStates::contractStateClassName.name).`in`(contractTypes)))
}
}
return emptySet()
}
private fun parse(sorting: Sort) {

View File

@ -336,78 +336,6 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
}
}
// TODO We shouldn't need to rewrite the query if we could modify the defaults.
private class QueryEditor<out T : ContractState>(val services: ServiceHub,
val lockId: UUID,
val contractType: Class<out T>) : IQueryCriteriaParser {
var alreadyHasVaultQuery: Boolean = false
var modifiedCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(contractStateTypes = setOf(contractType),
softLockingCondition = QueryCriteria.SoftLockingCondition(QueryCriteria.SoftLockingType.UNLOCKED_AND_SPECIFIED, listOf(lockId)),
status = Vault.StateStatus.UNCONSUMED)
override fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate> {
modifiedCriteria = criteria
return emptyList()
}
override fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate> {
modifiedCriteria = criteria
return emptyList()
}
override fun parseCriteria(criteria: QueryCriteria.LinearStateQueryCriteria): Collection<Predicate> {
modifiedCriteria = criteria
return emptyList()
}
override fun <L : PersistentState> parseCriteria(criteria: QueryCriteria.VaultCustomQueryCriteria<L>): Collection<Predicate> {
modifiedCriteria = criteria
return emptyList()
}
override fun parseCriteria(criteria: QueryCriteria.VaultQueryCriteria): Collection<Predicate> {
modifiedCriteria = criteria.copy(contractStateTypes = setOf(contractType),
softLockingCondition = QueryCriteria.SoftLockingCondition(QueryCriteria.SoftLockingType.UNLOCKED_AND_SPECIFIED, listOf(lockId)),
status = Vault.StateStatus.UNCONSUMED)
alreadyHasVaultQuery = true
return emptyList()
}
override fun parseOr(left: QueryCriteria, right: QueryCriteria): Collection<Predicate> {
parse(left)
val modifiedLeft = modifiedCriteria
parse(right)
val modifiedRight = modifiedCriteria
modifiedCriteria = modifiedLeft.or(modifiedRight)
return emptyList()
}
override fun parseAnd(left: QueryCriteria, right: QueryCriteria): Collection<Predicate> {
parse(left)
val modifiedLeft = modifiedCriteria
parse(right)
val modifiedRight = modifiedCriteria
modifiedCriteria = modifiedLeft.and(modifiedRight)
return emptyList()
}
override fun parse(criteria: QueryCriteria, sorting: Sort?): Collection<Predicate> {
val basicQuery = modifiedCriteria
criteria.visit(this)
modifiedCriteria = if (alreadyHasVaultQuery) modifiedCriteria else criteria.and(basicQuery)
return emptyList()
}
fun queryForEligibleStates(criteria: QueryCriteria): Vault.Page<T> {
val sortAttribute = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF)
val sorter = Sort(setOf(Sort.SortColumn(sortAttribute, Sort.Direction.ASC)))
parse(criteria, sorter)
return services.vaultQueryService.queryBy(contractType, modifiedCriteria, sorter)
}
}
@Suspendable
@Throws(StatesNotAvailableException::class)
override fun <T : FungibleAsset<U>, U : Any> tryLockFungibleStatesForSpending(lockId: UUID,
@ -418,9 +346,13 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
return emptyList()
}
// TODO This helper code re-writes the query to alter the defaults on things such as soft locks
// and then runs the query. Ideally we would not need to do this.
val results = QueryEditor(services, lockId, contractType).queryForEligibleStates(eligibleStatesQuery)
// Enrich QueryCriteria with additional default attributes (such as soft locks)
val sortAttribute = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF)
val sorter = Sort(setOf(Sort.SortColumn(sortAttribute, Sort.Direction.ASC)))
val enrichedCriteria = QueryCriteria.VaultQueryCriteria(
contractStateTypes = setOf(contractType),
softLockingCondition = QueryCriteria.SoftLockingCondition(QueryCriteria.SoftLockingType.UNLOCKED_AND_SPECIFIED, listOf(lockId)))
val results = services.vaultQueryService.queryBy(contractType, enrichedCriteria.and(eligibleStatesQuery), sorter)
var claimedAmount = 0L
val claimedStates = mutableListOf<StateAndRef<T>>()

View File

@ -192,8 +192,8 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
QueryCriteria vaultCriteria = new VaultQueryCriteria(status, contractStateTypes);
List<UUID> linearIds = Collections.singletonList(ids.getSecond().getId());
QueryCriteria linearCriteriaAll = new LinearStateQueryCriteria(null, linearIds);
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(null, null, dealIds);
QueryCriteria linearCriteriaAll = new LinearStateQueryCriteria(null, linearIds, null, status);
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(null, null, dealIds, status);
QueryCriteria compositeCriteria1 = dealCriteriaAll.or(linearCriteriaAll);
QueryCriteria compositeCriteria2 = vaultCriteria.and(compositeCriteria1);

View File

@ -11,7 +11,6 @@ import net.corda.core.node.services.vault.*
import net.corda.core.node.services.vault.QueryCriteria.*
import net.corda.core.utilities.*
import net.corda.finance.*
import net.corda.node.services.schema.NodeSchemaService
import net.corda.finance.contracts.CommercialPaper
import net.corda.finance.contracts.Commodity
import net.corda.finance.contracts.DealState
@ -1338,8 +1337,8 @@ class VaultQueryTests : TestDependencyInjectionBase() {
val sorting = Sort(setOf(Sort.SortColumn(SortAttribute.Standard(Sort.LinearStateAttribute.EXTERNAL_ID), Sort.Direction.DESC)))
val results = vaultQuerySvc.queryBy<LinearState>(compositeCriteria, sorting = sorting)
assertThat(results.statesMetadata).hasSize(13)
assertThat(results.states).hasSize(13)
assertThat(results.statesMetadata).hasSize(4)
assertThat(results.states).hasSize(4)
}
}
@ -1860,13 +1859,13 @@ class VaultQueryTests : TestDependencyInjectionBase() {
services.fillWithSomeTestLinearStates(1, "TEST1")
val aState = services.fillWithSomeTestLinearStates(1, "TEST2").states
services.consumeLinearStates(aState.toList(), DUMMY_NOTARY)
services.fillWithSomeTestLinearStates(1, "TEST3").states.first().state.data.linearId.id
services.fillWithSomeTestLinearStates(1, "TEST1").states.first().state.data.linearId.id
// 2 unconsumed states with same external ID, 1 with different external ID
// 2 unconsumed states with same external ID, 1 consumed with different external ID
}
database.transaction {
val results = builder {
val externalIdCondition = VaultSchemaV1.VaultLinearStates::externalId.equal("TEST2")
val externalIdCondition = VaultSchemaV1.VaultLinearStates::externalId.equal("TEST1")
val externalIdCustomCriteria = VaultCustomQueryCriteria(externalIdCondition)
val uuidCondition = VaultSchemaV1.VaultLinearStates::uuid.equal(uuid)
@ -1875,8 +1874,8 @@ class VaultQueryTests : TestDependencyInjectionBase() {
val criteria = externalIdCustomCriteria or uuidCustomCriteria
vaultQuerySvc.queryBy<LinearState>(criteria)
}
assertThat(results.statesMetadata).hasSize(3)
assertThat(results.states).hasSize(3)
assertThat(results.statesMetadata).hasSize(2)
assertThat(results.states).hasSize(2)
}
}
@ -1957,6 +1956,34 @@ class VaultQueryTests : TestDependencyInjectionBase() {
}
}
@Test
fun `enriched and overridden composite query handles defaults correctly`() {
database.transaction {
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L))
services.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices)
services.fillWithSomeTestLinearStates(1, "ABC")
services.fillWithSomeTestDeals(listOf("123"))
}
database.transaction {
// Base criteria
val baseCriteria = VaultQueryCriteria(notary = listOf(DUMMY_NOTARY),
status = Vault.StateStatus.CONSUMED)
// Enrich and override QueryCriteria with additional default attributes (such as soft locks)
val enrichedCriteria = VaultQueryCriteria(contractStateTypes = setOf(DealState::class.java), // enrich
softLockingCondition = QueryCriteria.SoftLockingCondition(QueryCriteria.SoftLockingType.UNLOCKED_AND_SPECIFIED, listOf(UUID.randomUUID())),
status = Vault.StateStatus.UNCONSUMED) // override
// Sorting
val sortAttribute = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF)
val sorter = Sort(setOf(Sort.SortColumn(sortAttribute, Sort.Direction.ASC)))
// Execute query
val results = services.vaultQueryService.queryBy<FungibleAsset<*>>(baseCriteria and enrichedCriteria, sorter).states
assertThat(results).hasSize(4)
}
}
/**
* Dynamic trackBy() tests
*/
@ -1965,7 +1992,9 @@ class VaultQueryTests : TestDependencyInjectionBase() {
fun trackCashStates_unconsumed() {
val updates =
database.transaction {
// DOCSTART VaultQueryExample15
vaultQuerySvc.trackBy<Cash.State>().updates // UNCONSUMED default
// DOCEND VaultQueryExample15
}
val (linearStates,dealStates) =
database.transaction {