mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
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:
parent
1750ab07af
commit
a3dbbc173b
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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>>()
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user