mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +00:00
Vault Query Aggregate Function support (#950)
* Partial (ie. incomplete) implementation of Aggregate Functions. * Completed implementation of Aggregate Functions (sum, count, max, min, avg) with optional grouping. * Completed Java DSL and associated JUnit tests. * Added optional sorting by aggregate function. * Added Jvm filename annotation on QueryCriteriaUtils. * Added documentation (API and RST with code samples). * Incorporating feedback from MH - improved readability in structuring Java and/or queries. * Remove redundant import. * Removed redundant commas. * Streamlined expression parsing (in doing so, remove the ugly try-catch raised by RP in PR review comments.) * Added JvmStatic and JvmOverloads to Java DSL; removed duplicate Kotlin DSL functions using default params; changed varargs to lists due to ambiguity * Fix missing imports after rebase from master. * Fix errors following rebase from master. * Updates on expression handling following feedback from RP.
This commit is contained in:
@ -35,6 +35,7 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
|
||||
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 aggregateExpressions = mutableListOf<Expression<*>>()
|
||||
|
||||
var stateTypes: Vault.StateStatus = Vault.StateStatus.UNCONSUMED
|
||||
|
||||
@ -78,7 +79,7 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
|
||||
QueryCriteria.TimeInstantType.CONSUMED -> Column.Kotlin(VaultSchemaV1.VaultStates::consumedTime)
|
||||
}
|
||||
val expression = CriteriaExpression.ColumnPredicateExpression(timeColumn, timeCondition.predicate)
|
||||
predicateSet.add(expressionToPredicate(vaultStates, expression))
|
||||
predicateSet.add(parseExpression(vaultStates, expression) as Predicate)
|
||||
}
|
||||
return predicateSet
|
||||
}
|
||||
@ -127,32 +128,75 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
|
||||
NullOperator.NOT_NULL -> criteriaBuilder.isNotNull(column)
|
||||
}
|
||||
}
|
||||
else -> throw VaultQueryException("Not expecting $columnPredicate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return : Expression<Boolean> -> : Predicate
|
||||
*/
|
||||
private fun <O, R> expressionToExpression(root: Root<O>, expression: CriteriaExpression<O, R>): Expression<R> {
|
||||
private fun <O> parseExpression(entityRoot: Root<O>, expression: CriteriaExpression<O, Boolean>, predicateSet: MutableSet<Predicate>) {
|
||||
if (expression is CriteriaExpression.AggregateFunctionExpression<O,*>) {
|
||||
parseAggregateFunction(entityRoot, expression)
|
||||
} else {
|
||||
predicateSet.add(parseExpression(entityRoot, expression) as Predicate)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <O, R> parseExpression(root: Root<O>, expression: CriteriaExpression<O, R>): Expression<Boolean> {
|
||||
return when (expression) {
|
||||
is CriteriaExpression.BinaryLogical -> {
|
||||
val leftPredicate = expressionToExpression(root, expression.left)
|
||||
val rightPredicate = expressionToExpression(root, expression.right)
|
||||
val leftPredicate = parseExpression(root, expression.left)
|
||||
val rightPredicate = parseExpression(root, expression.right)
|
||||
when (expression.operator) {
|
||||
BinaryLogicalOperator.AND -> criteriaBuilder.and(leftPredicate, rightPredicate) as Expression<R>
|
||||
BinaryLogicalOperator.OR -> criteriaBuilder.or(leftPredicate, rightPredicate) as Expression<R>
|
||||
BinaryLogicalOperator.AND -> criteriaBuilder.and(leftPredicate, rightPredicate)
|
||||
BinaryLogicalOperator.OR -> criteriaBuilder.or(leftPredicate, rightPredicate)
|
||||
}
|
||||
}
|
||||
is CriteriaExpression.Not -> criteriaBuilder.not(expressionToExpression(root, expression.expression)) as Expression<R>
|
||||
is CriteriaExpression.Not -> criteriaBuilder.not(parseExpression(root, expression.expression))
|
||||
is CriteriaExpression.ColumnPredicateExpression<O, *> -> {
|
||||
val column = root.get<Any?>(getColumnName(expression.column))
|
||||
columnPredicateToPredicate(column, expression.predicate) as Expression<R>
|
||||
columnPredicateToPredicate(column, expression.predicate)
|
||||
}
|
||||
else -> throw VaultQueryException("Unexpected expression: $expression")
|
||||
}
|
||||
}
|
||||
|
||||
private fun <O> expressionToPredicate(root: Root<O>, expression: CriteriaExpression<O, Boolean>): Predicate {
|
||||
return expressionToExpression(root, expression) as Predicate
|
||||
private fun <O, R> parseAggregateFunction(root: Root<O>, expression: CriteriaExpression.AggregateFunctionExpression<O, R>): Expression<out Any?>? {
|
||||
val column = root.get<Any?>(getColumnName(expression.column))
|
||||
val columnPredicate = expression.predicate
|
||||
when (columnPredicate) {
|
||||
is ColumnPredicate.AggregateFunction -> {
|
||||
column as Path<Long?>?
|
||||
val aggregateExpression =
|
||||
when (columnPredicate.type) {
|
||||
AggregateFunctionType.SUM -> criteriaBuilder.sum(column)
|
||||
AggregateFunctionType.AVG -> criteriaBuilder.avg(column)
|
||||
AggregateFunctionType.COUNT -> criteriaBuilder.count(column)
|
||||
AggregateFunctionType.MAX -> criteriaBuilder.max(column)
|
||||
AggregateFunctionType.MIN -> criteriaBuilder.min(column)
|
||||
}
|
||||
aggregateExpressions.add(aggregateExpression)
|
||||
// optionally order by this aggregate function
|
||||
expression.orderBy?.let {
|
||||
val orderCriteria =
|
||||
when (expression.orderBy!!) {
|
||||
Sort.Direction.ASC -> criteriaBuilder.asc(aggregateExpression)
|
||||
Sort.Direction.DESC -> criteriaBuilder.desc(aggregateExpression)
|
||||
}
|
||||
criteriaQuery.orderBy(orderCriteria)
|
||||
}
|
||||
// add optional group by clauses
|
||||
expression.groupByColumns?.let { columns ->
|
||||
val groupByExpressions =
|
||||
columns.map { column ->
|
||||
val path = root.get<Any?>(getColumnName(column))
|
||||
aggregateExpressions.add(path)
|
||||
path
|
||||
}
|
||||
criteriaQuery.groupBy(groupByExpressions)
|
||||
}
|
||||
return aggregateExpression
|
||||
}
|
||||
else -> throw VaultQueryException("Not expecting $columnPredicate")
|
||||
}
|
||||
}
|
||||
|
||||
override fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria) : Collection<Predicate> {
|
||||
@ -254,7 +298,8 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
|
||||
val joinPredicate = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<PersistentStateRef>("stateRef"))
|
||||
joinPredicates.add(joinPredicate)
|
||||
|
||||
predicateSet.add(expressionToPredicate(entityRoot, criteria.expression))
|
||||
// resolve general criteria expressions
|
||||
parseExpression(entityRoot, criteria.expression, predicateSet)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
e.message?.let { message ->
|
||||
@ -303,7 +348,11 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
|
||||
parse(sorting)
|
||||
}
|
||||
|
||||
val selections = listOf(vaultStates).plus(rootEntities.map { it.value })
|
||||
val selections =
|
||||
if (aggregateExpressions.isEmpty())
|
||||
listOf(vaultStates).plus(rootEntities.map { it.value })
|
||||
else
|
||||
aggregateExpressions
|
||||
criteriaQuery.multiselect(selections)
|
||||
val combinedPredicates = joinPredicates.plus(predicateSet)
|
||||
criteriaQuery.where(*combinedPredicates.toTypedArray())
|
||||
|
@ -18,19 +18,20 @@ import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.storageKryo
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.services.database.HibernateConfiguration
|
||||
import net.corda.node.services.vault.schemas.jpa.VaultSchemaV1
|
||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||
import rx.subjects.PublishSubject
|
||||
import java.lang.Exception
|
||||
import java.util.*
|
||||
import javax.persistence.EntityManager
|
||||
import javax.persistence.Tuple
|
||||
|
||||
|
||||
class HibernateVaultQueryImpl(hibernateConfig: HibernateConfiguration,
|
||||
val updatesPublisher: PublishSubject<Vault.Update>) : SingletonSerializeAsToken(), VaultQueryService {
|
||||
|
||||
companion object {
|
||||
val log = loggerFor<HibernateVaultQueryImpl>()
|
||||
}
|
||||
@ -80,17 +81,24 @@ class HibernateVaultQueryImpl(hibernateConfig: HibernateConfiguration,
|
||||
val results = query.resultList
|
||||
val statesAndRefs: MutableList<StateAndRef<*>> = mutableListOf()
|
||||
val statesMeta: MutableList<Vault.StateMetadata> = mutableListOf()
|
||||
val otherResults: MutableList<Any> = mutableListOf()
|
||||
|
||||
results.asSequence()
|
||||
.forEach { it ->
|
||||
val it = it[0] as VaultSchemaV1.VaultStates
|
||||
val stateRef = StateRef(SecureHash.parse(it.stateRef!!.txId!!), it.stateRef!!.index!!)
|
||||
val state = it.contractState.deserialize<TransactionState<T>>(storageKryo())
|
||||
statesMeta.add(Vault.StateMetadata(stateRef, it.contractStateClassName, it.recordedTime, it.consumedTime, it.stateStatus, it.notaryName, it.notaryKey, it.lockId, it.lockUpdateTime))
|
||||
statesAndRefs.add(StateAndRef(state, stateRef))
|
||||
if (it[0] is VaultSchemaV1.VaultStates) {
|
||||
val it = it[0] as VaultSchemaV1.VaultStates
|
||||
val stateRef = StateRef(SecureHash.parse(it.stateRef!!.txId!!), it.stateRef!!.index!!)
|
||||
val state = it.contractState.deserialize<TransactionState<T>>(storageKryo())
|
||||
statesMeta.add(Vault.StateMetadata(stateRef, it.contractStateClassName, it.recordedTime, it.consumedTime, it.stateStatus, it.notaryName, it.notaryKey, it.lockId, it.lockUpdateTime))
|
||||
statesAndRefs.add(StateAndRef(state, stateRef))
|
||||
}
|
||||
else {
|
||||
log.debug { "OtherResults: ${Arrays.toString(it.toArray())}" }
|
||||
otherResults.addAll(it.toArray().asList())
|
||||
}
|
||||
}
|
||||
|
||||
return Vault.Page(states = statesAndRefs, statesMetadata = statesMeta, pageable = paging, stateTypes = criteriaParser.stateTypes, totalStatesAvailable = totalStates) as Vault.Page<T>
|
||||
return Vault.Page(states = statesAndRefs, statesMetadata = statesMeta, pageable = paging, stateTypes = criteriaParser.stateTypes, totalStatesAvailable = totalStates, otherResults = otherResults) as Vault.Page<T>
|
||||
|
||||
} catch (e: Exception) {
|
||||
log.error(e.message)
|
||||
|
@ -6,7 +6,7 @@ import net.corda.contracts.DealState;
|
||||
import net.corda.contracts.asset.Cash;
|
||||
import net.corda.core.contracts.*;
|
||||
import net.corda.core.contracts.testing.DummyLinearContract;
|
||||
import net.corda.core.crypto.SecureHash;
|
||||
import net.corda.core.crypto.*;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import net.corda.core.messaging.DataFeed;
|
||||
import net.corda.core.node.services.Vault;
|
||||
@ -45,10 +45,11 @@ import java.util.stream.StreamSupport;
|
||||
|
||||
import static net.corda.contracts.asset.CashKt.getDUMMY_CASH_ISSUER;
|
||||
import static net.corda.contracts.asset.CashKt.getDUMMY_CASH_ISSUER_KEY;
|
||||
import static net.corda.testing.CoreTestUtils.getBOC;
|
||||
import static net.corda.testing.CoreTestUtils.getBOC_KEY;
|
||||
import static net.corda.testing.CoreTestUtils.getBOC_PUBKEY;
|
||||
import static net.corda.core.contracts.ContractsDSL.USD;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaKt.and;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaKt.or;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaUtilsKt.getMAX_PAGE_SIZE;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaUtils.getMAX_PAGE_SIZE;
|
||||
import static net.corda.node.utilities.DatabaseSupportKt.configureDatabase;
|
||||
import static net.corda.node.utilities.DatabaseSupportKt.transaction;
|
||||
import static net.corda.testing.CoreTestUtils.getMEGA_CORP;
|
||||
@ -188,8 +189,8 @@ public class VaultQueryJavaTests {
|
||||
QueryCriteria linearCriteriaAll = new LinearStateQueryCriteria(null, linearIds);
|
||||
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(null, null, dealIds);
|
||||
|
||||
QueryCriteria compositeCriteria1 = or(dealCriteriaAll, linearCriteriaAll);
|
||||
QueryCriteria compositeCriteria2 = and(vaultCriteria, compositeCriteria1);
|
||||
QueryCriteria compositeCriteria1 = dealCriteriaAll.or(linearCriteriaAll);
|
||||
QueryCriteria compositeCriteria2 = vaultCriteria.and(compositeCriteria1);
|
||||
|
||||
PageSpecification pageSpec = new PageSpecification(0, getMAX_PAGE_SIZE());
|
||||
Sort.SortColumn sortByUid = new Sort.SortColumn(new SortAttribute.Standard(Sort.LinearStateAttribute.UUID), Sort.Direction.DESC);
|
||||
@ -224,14 +225,14 @@ public class VaultQueryJavaTests {
|
||||
Field attributeCurrency = CashSchemaV1.PersistentCashState.class.getDeclaredField("currency");
|
||||
Field attributeQuantity = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
|
||||
|
||||
CriteriaExpression currencyIndex = Builder.INSTANCE.equal(attributeCurrency, "USD");
|
||||
CriteriaExpression quantityIndex = Builder.INSTANCE.greaterThanOrEqual(attributeQuantity, 10L);
|
||||
CriteriaExpression currencyIndex = Builder.equal(attributeCurrency, "USD");
|
||||
CriteriaExpression quantityIndex = Builder.greaterThanOrEqual(attributeQuantity, 10L);
|
||||
|
||||
QueryCriteria customCriteria2 = new VaultCustomQueryCriteria(quantityIndex);
|
||||
QueryCriteria customCriteria1 = new VaultCustomQueryCriteria(currencyIndex);
|
||||
|
||||
|
||||
QueryCriteria criteria = QueryCriteriaKt.and(QueryCriteriaKt.and(generalCriteria, customCriteria1), customCriteria2);
|
||||
QueryCriteria criteria = generalCriteria.and(customCriteria1).and(customCriteria2);
|
||||
Vault.Page<ContractState> results = vaultQuerySvc.queryBy(Cash.State.class, criteria);
|
||||
// DOCEND VaultJavaQueryExample3
|
||||
|
||||
@ -297,8 +298,8 @@ public class VaultQueryJavaTests {
|
||||
List<AbstractParty> dealParty = Collections.singletonList(getMEGA_CORP());
|
||||
QueryCriteria dealCriteria = new LinearStateQueryCriteria(dealParty, null, dealIds);
|
||||
QueryCriteria linearCriteria = new LinearStateQueryCriteria(dealParty, linearIds, null);
|
||||
QueryCriteria dealOrLinearIdCriteria = or(dealCriteria, linearCriteria);
|
||||
QueryCriteria compositeCriteria = and(dealOrLinearIdCriteria, vaultCriteria);
|
||||
QueryCriteria dealOrLinearIdCriteria = dealCriteria.or(linearCriteria);
|
||||
QueryCriteria compositeCriteria = dealOrLinearIdCriteria.and(vaultCriteria);
|
||||
|
||||
PageSpecification pageSpec = new PageSpecification(0, getMAX_PAGE_SIZE());
|
||||
Sort.SortColumn sortByUid = new Sort.SortColumn(new SortAttribute.Standard(Sort.LinearStateAttribute.UUID), Sort.Direction.DESC);
|
||||
@ -374,4 +375,169 @@ public class VaultQueryJavaTests {
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregation Functions
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void aggregateFunctionsWithoutGroupClause() {
|
||||
transaction(database, tx -> {
|
||||
|
||||
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
||||
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
||||
Amount<Currency> dollars300 = new Amount<>(300, Currency.getInstance("USD"));
|
||||
Amount<Currency> pounds = new Amount<>(400, Currency.getInstance("GBP"));
|
||||
Amount<Currency> swissfrancs = new Amount<>(500, Currency.getInstance("CHF"));
|
||||
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars100, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars200, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars300, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, pounds, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, swissfrancs, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
|
||||
try {
|
||||
// DOCSTART VaultJavaQueryExample21
|
||||
Field pennies = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
|
||||
|
||||
QueryCriteria sumCriteria = new VaultCustomQueryCriteria(Builder.sum(pennies));
|
||||
QueryCriteria countCriteria = new VaultCustomQueryCriteria(Builder.count(pennies));
|
||||
QueryCriteria maxCriteria = new VaultCustomQueryCriteria(Builder.max(pennies));
|
||||
QueryCriteria minCriteria = new VaultCustomQueryCriteria(Builder.min(pennies));
|
||||
QueryCriteria avgCriteria = new VaultCustomQueryCriteria(Builder.avg(pennies));
|
||||
|
||||
QueryCriteria criteria = sumCriteria.and(countCriteria).and(maxCriteria).and(minCriteria).and(avgCriteria);
|
||||
Vault.Page<Cash.State> results = vaultQuerySvc.queryBy(Cash.State.class, criteria);
|
||||
// DOCEND VaultJavaQueryExample21
|
||||
|
||||
assertThat(results.getOtherResults()).hasSize(5);
|
||||
assertThat(results.getOtherResults().get(0)).isEqualTo(1500L);
|
||||
assertThat(results.getOtherResults().get(1)).isEqualTo(15L);
|
||||
assertThat(results.getOtherResults().get(2)).isEqualTo(113L);
|
||||
assertThat(results.getOtherResults().get(3)).isEqualTo(87L);
|
||||
assertThat(results.getOtherResults().get(4)).isEqualTo(100.0);
|
||||
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateFunctionsWithSingleGroupClause() {
|
||||
transaction(database, tx -> {
|
||||
|
||||
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
||||
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
||||
Amount<Currency> dollars300 = new Amount<>(300, Currency.getInstance("USD"));
|
||||
Amount<Currency> pounds = new Amount<>(400, Currency.getInstance("GBP"));
|
||||
Amount<Currency> swissfrancs = new Amount<>(500, Currency.getInstance("CHF"));
|
||||
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars100, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars200, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars300, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, pounds, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, swissfrancs, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
|
||||
try {
|
||||
// DOCSTART VaultJavaQueryExample22
|
||||
Field pennies = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
|
||||
Field currency = CashSchemaV1.PersistentCashState.class.getDeclaredField("currency");
|
||||
|
||||
QueryCriteria sumCriteria = new VaultCustomQueryCriteria(Builder.sum(pennies, Arrays.asList(currency)));
|
||||
QueryCriteria countCriteria = new VaultCustomQueryCriteria(Builder.count(pennies));
|
||||
QueryCriteria maxCriteria = new VaultCustomQueryCriteria(Builder.max(pennies, Arrays.asList(currency)));
|
||||
QueryCriteria minCriteria = new VaultCustomQueryCriteria(Builder.min(pennies, Arrays.asList(currency)));
|
||||
QueryCriteria avgCriteria = new VaultCustomQueryCriteria(Builder.avg(pennies, Arrays.asList(currency)));
|
||||
|
||||
QueryCriteria criteria = sumCriteria.and(countCriteria).and(maxCriteria).and(minCriteria).and(avgCriteria);
|
||||
Vault.Page<Cash.State> results = vaultQuerySvc.queryBy(Cash.State.class, criteria);
|
||||
// DOCEND VaultJavaQueryExample22
|
||||
|
||||
assertThat(results.getOtherResults()).hasSize(27);
|
||||
/** CHF */
|
||||
assertThat(results.getOtherResults().get(0)).isEqualTo(500L);
|
||||
assertThat(results.getOtherResults().get(1)).isEqualTo("CHF");
|
||||
assertThat(results.getOtherResults().get(2)).isEqualTo(5L);
|
||||
assertThat(results.getOtherResults().get(3)).isEqualTo(102L);
|
||||
assertThat(results.getOtherResults().get(4)).isEqualTo("CHF");
|
||||
assertThat(results.getOtherResults().get(5)).isEqualTo(94L);
|
||||
assertThat(results.getOtherResults().get(6)).isEqualTo("CHF");
|
||||
assertThat(results.getOtherResults().get(7)).isEqualTo(100.00);
|
||||
assertThat(results.getOtherResults().get(8)).isEqualTo("CHF");
|
||||
/** GBP */
|
||||
assertThat(results.getOtherResults().get(9)).isEqualTo(400L);
|
||||
assertThat(results.getOtherResults().get(10)).isEqualTo("GBP");
|
||||
assertThat(results.getOtherResults().get(11)).isEqualTo(4L);
|
||||
assertThat(results.getOtherResults().get(12)).isEqualTo(103L);
|
||||
assertThat(results.getOtherResults().get(13)).isEqualTo("GBP");
|
||||
assertThat(results.getOtherResults().get(14)).isEqualTo(93L);
|
||||
assertThat(results.getOtherResults().get(15)).isEqualTo("GBP");
|
||||
assertThat(results.getOtherResults().get(16)).isEqualTo(100.0);
|
||||
assertThat(results.getOtherResults().get(17)).isEqualTo("GBP");
|
||||
/** USD */
|
||||
assertThat(results.getOtherResults().get(18)).isEqualTo(600L);
|
||||
assertThat(results.getOtherResults().get(19)).isEqualTo("USD");
|
||||
assertThat(results.getOtherResults().get(20)).isEqualTo(6L);
|
||||
assertThat(results.getOtherResults().get(21)).isEqualTo(113L);
|
||||
assertThat(results.getOtherResults().get(22)).isEqualTo("USD");
|
||||
assertThat(results.getOtherResults().get(23)).isEqualTo(87L);
|
||||
assertThat(results.getOtherResults().get(24)).isEqualTo("USD");
|
||||
assertThat(results.getOtherResults().get(25)).isEqualTo(100.0);
|
||||
assertThat(results.getOtherResults().get(26)).isEqualTo("USD");
|
||||
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateFunctionsSumByIssuerAndCurrencyAndSortByAggregateSum() {
|
||||
transaction(database, tx -> {
|
||||
|
||||
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
||||
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
||||
Amount<Currency> pounds300 = new Amount<>(300, Currency.getInstance("GBP"));
|
||||
Amount<Currency> pounds400 = new Amount<>(400, Currency.getInstance("GBP"));
|
||||
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars100, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, dollars200, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), new OpaqueBytes("1".getBytes()), null, getBOC().ref(new OpaqueBytes("1".getBytes())), getBOC_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, pounds300, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), new OpaqueBytes("1".getBytes()), null, getDUMMY_CASH_ISSUER(), getDUMMY_CASH_ISSUER_KEY());
|
||||
VaultFiller.fillWithSomeTestCash(services, pounds400, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), new OpaqueBytes("1".getBytes()), null, getBOC().ref(new OpaqueBytes("1".getBytes())), getBOC_KEY());
|
||||
|
||||
try {
|
||||
// DOCSTART VaultJavaQueryExample23
|
||||
Field pennies = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
|
||||
Field currency = CashSchemaV1.PersistentCashState.class.getDeclaredField("currency");
|
||||
Field issuerParty = CashSchemaV1.PersistentCashState.class.getDeclaredField("issuerParty");
|
||||
|
||||
QueryCriteria sumCriteria = new VaultCustomQueryCriteria(Builder.sum(pennies, Arrays.asList(issuerParty, currency), Sort.Direction.DESC));
|
||||
|
||||
Vault.Page<Cash.State> results = vaultQuerySvc.queryBy(Cash.State.class, sumCriteria);
|
||||
// DOCEND VaultJavaQueryExample23
|
||||
|
||||
assertThat(results.getOtherResults()).hasSize(12);
|
||||
|
||||
assertThat(results.getOtherResults().get(0)).isEqualTo(400L);
|
||||
assertThat(results.getOtherResults().get(1)).isEqualTo(EncodingUtils.toBase58String(getBOC_PUBKEY()));
|
||||
assertThat(results.getOtherResults().get(2)).isEqualTo("GBP");
|
||||
assertThat(results.getOtherResults().get(3)).isEqualTo(300L);
|
||||
assertThat(results.getOtherResults().get(4)).isEqualTo(EncodingUtils.toBase58String(getDUMMY_CASH_ISSUER().getParty().getOwningKey()));
|
||||
assertThat(results.getOtherResults().get(5)).isEqualTo("GBP");
|
||||
assertThat(results.getOtherResults().get(6)).isEqualTo(200L);
|
||||
assertThat(results.getOtherResults().get(7)).isEqualTo(EncodingUtils.toBase58String(getBOC_PUBKEY()));
|
||||
assertThat(results.getOtherResults().get(8)).isEqualTo("USD");
|
||||
assertThat(results.getOtherResults().get(9)).isEqualTo(100L);
|
||||
assertThat(results.getOtherResults().get(10)).isEqualTo(EncodingUtils.toBase58String(getDUMMY_CASH_ISSUER().getParty().getOwningKey()));
|
||||
assertThat(results.getOtherResults().get(11)).isEqualTo("USD");
|
||||
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.node.services.database
|
||||
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||
import net.corda.contracts.asset.DummyFungibleContract
|
||||
import net.corda.testing.contracts.consumeCash
|
||||
import net.corda.testing.contracts.fillWithSomeTestCash
|
||||
@ -31,6 +32,8 @@ import net.corda.schemas.CashSchemaV1
|
||||
import net.corda.schemas.SampleCashSchemaV2
|
||||
import net.corda.schemas.SampleCashSchemaV3
|
||||
import net.corda.testing.BOB_PUBKEY
|
||||
import net.corda.testing.BOC
|
||||
import net.corda.testing.BOC_KEY
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.makeTestDataSourceProperties
|
||||
import org.assertj.core.api.Assertions
|
||||
@ -301,6 +304,109 @@ class HibernateConfigurationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `calculate cash balances`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L)) // +$100 = $200
|
||||
services.fillWithSomeTestCash(50.POUNDS, DUMMY_NOTARY, 5, 5, Random(0L)) // £50 = £50
|
||||
services.fillWithSomeTestCash(25.POUNDS, DUMMY_NOTARY, 5, 5, Random(0L)) // +£25 = £175
|
||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, DUMMY_NOTARY, 10, 10, Random(0L)) // CHF500 = CHF500
|
||||
services.fillWithSomeTestCash(250.SWISS_FRANCS, DUMMY_NOTARY, 5, 5, Random(0L)) // +CHF250 = CHF750
|
||||
}
|
||||
|
||||
// structure query
|
||||
val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java)
|
||||
val cashStates = criteriaQuery.from(CashSchemaV1.PersistentCashState::class.java)
|
||||
|
||||
// aggregate function
|
||||
criteriaQuery.multiselect(cashStates.get<String>("currency"),
|
||||
criteriaBuilder.sum(cashStates.get<Long>("pennies")))
|
||||
// group by
|
||||
criteriaQuery.groupBy(cashStates.get<String>("currency"))
|
||||
|
||||
// execute query
|
||||
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
||||
|
||||
queryResults.forEach { tuple -> println("${tuple.get(0)} = ${tuple.get(1)}") }
|
||||
|
||||
assertThat(queryResults[0].get(0)).isEqualTo("CHF")
|
||||
assertThat(queryResults[0].get(1)).isEqualTo(75000L)
|
||||
assertThat(queryResults[1].get(0)).isEqualTo("GBP")
|
||||
assertThat(queryResults[1].get(1)).isEqualTo(7500L)
|
||||
assertThat(queryResults[2].get(0)).isEqualTo("USD")
|
||||
assertThat(queryResults[2].get(1)).isEqualTo(20000L)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `calculate cash balance for single currency`() {
|
||||
database.transaction {
|
||||
services.fillWithSomeTestCash(50.POUNDS, DUMMY_NOTARY, 5, 5, Random(0L)) // £50 = £50
|
||||
services.fillWithSomeTestCash(25.POUNDS, DUMMY_NOTARY, 5, 5, Random(0L)) // +£25 = £175
|
||||
}
|
||||
|
||||
// structure query
|
||||
val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java)
|
||||
val cashStates = criteriaQuery.from(CashSchemaV1.PersistentCashState::class.java)
|
||||
|
||||
// aggregate function
|
||||
criteriaQuery.multiselect(cashStates.get<String>("currency"),
|
||||
criteriaBuilder.sum(cashStates.get<Long>("pennies")))
|
||||
|
||||
// where
|
||||
criteriaQuery.where(criteriaBuilder.equal(cashStates.get<String>("currency"), "GBP"))
|
||||
|
||||
// group by
|
||||
criteriaQuery.groupBy(cashStates.get<String>("currency"))
|
||||
|
||||
// execute query
|
||||
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
||||
|
||||
queryResults.forEach { tuple -> println("${tuple.get(0)} = ${tuple.get(1)}") }
|
||||
|
||||
assertThat(queryResults[0].get(0)).isEqualTo("GBP")
|
||||
assertThat(queryResults[0].get(1)).isEqualTo(7500L)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `calculate and order by cash balance for owner and currency`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(200.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1), issuerKey = BOC_KEY)
|
||||
services.fillWithSomeTestCash(300.POUNDS, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER)
|
||||
services.fillWithSomeTestCash(400.POUNDS, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2), issuerKey = BOC_KEY)
|
||||
}
|
||||
|
||||
// structure query
|
||||
val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java)
|
||||
val cashStates = criteriaQuery.from(CashSchemaV1.PersistentCashState::class.java)
|
||||
|
||||
// aggregate function
|
||||
criteriaQuery.multiselect(cashStates.get<String>("currency"),
|
||||
criteriaBuilder.sum(cashStates.get<Long>("pennies")))
|
||||
|
||||
// group by
|
||||
criteriaQuery.groupBy(cashStates.get<String>("issuerParty"), cashStates.get<String>("currency"))
|
||||
|
||||
// order by
|
||||
criteriaQuery.orderBy(criteriaBuilder.desc(criteriaBuilder.sum(cashStates.get<Long>("pennies"))))
|
||||
|
||||
// execute query
|
||||
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
||||
|
||||
queryResults.forEach { tuple -> println("${tuple.get(0)} = ${tuple.get(1)}") }
|
||||
|
||||
assertThat(queryResults).hasSize(4)
|
||||
assertThat(queryResults[0].get(0)).isEqualTo("GBP")
|
||||
assertThat(queryResults[0].get(1)).isEqualTo(40000L)
|
||||
assertThat(queryResults[1].get(0)).isEqualTo("GBP")
|
||||
assertThat(queryResults[1].get(1)).isEqualTo(30000L)
|
||||
assertThat(queryResults[2].get(0)).isEqualTo("USD")
|
||||
assertThat(queryResults[2].get(1)).isEqualTo(20000L)
|
||||
assertThat(queryResults[3].get(0)).isEqualTo("USD")
|
||||
assertThat(queryResults[3].get(1)).isEqualTo(10000L)
|
||||
}
|
||||
|
||||
/**
|
||||
* CashSchemaV2 = optimised Cash schema (extending FungibleState)
|
||||
*/
|
||||
|
@ -3,12 +3,12 @@ package net.corda.node.services.vault
|
||||
import net.corda.contracts.CommercialPaper
|
||||
import net.corda.contracts.Commodity
|
||||
import net.corda.contracts.DealState
|
||||
import net.corda.contracts.DummyDealContract
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.testing.DummyLinearContract
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.days
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.*
|
||||
@ -18,9 +18,6 @@ import net.corda.core.schemas.testing.DummyLinearStateSchemaV1
|
||||
import net.corda.core.seconds
|
||||
import net.corda.core.serialization.OpaqueBytes
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.DUMMY_NOTARY_KEY
|
||||
import net.corda.testing.TEST_TX_TIME
|
||||
import net.corda.node.services.database.HibernateConfiguration
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.services.vault.schemas.jpa.VaultSchemaV1
|
||||
@ -556,6 +553,143 @@ class VaultQueryTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `aggregate functions without group clause`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(200.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||
services.fillWithSomeTestCash(300.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(400.POUNDS, DUMMY_NOTARY, 4, 4, Random(0L))
|
||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, DUMMY_NOTARY, 5, 5, Random(0L))
|
||||
|
||||
// DOCSTART VaultQueryExample21
|
||||
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum() }
|
||||
val sumCriteria = VaultCustomQueryCriteria(sum)
|
||||
|
||||
val count = builder { CashSchemaV1.PersistentCashState::pennies.count() }
|
||||
val countCriteria = VaultCustomQueryCriteria(count)
|
||||
|
||||
val max = builder { CashSchemaV1.PersistentCashState::pennies.max() }
|
||||
val maxCriteria = VaultCustomQueryCriteria(max)
|
||||
|
||||
val min = builder { CashSchemaV1.PersistentCashState::pennies.min() }
|
||||
val minCriteria = VaultCustomQueryCriteria(min)
|
||||
|
||||
val avg = builder { CashSchemaV1.PersistentCashState::pennies.avg() }
|
||||
val avgCriteria = VaultCustomQueryCriteria(avg)
|
||||
|
||||
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(sumCriteria
|
||||
.and(countCriteria)
|
||||
.and(maxCriteria)
|
||||
.and(minCriteria)
|
||||
.and(avgCriteria))
|
||||
// DOCEND VaultQueryExample21
|
||||
|
||||
assertThat(results.otherResults).hasSize(5)
|
||||
assertThat(results.otherResults[0]).isEqualTo(150000L)
|
||||
assertThat(results.otherResults[1]).isEqualTo(15L)
|
||||
assertThat(results.otherResults[2]).isEqualTo(11298L)
|
||||
assertThat(results.otherResults[3]).isEqualTo(8702L)
|
||||
assertThat(results.otherResults[4]).isEqualTo(10000.0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `aggregate functions with single group clause`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(200.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||
services.fillWithSomeTestCash(300.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(400.POUNDS, DUMMY_NOTARY, 4, 4, Random(0L))
|
||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, DUMMY_NOTARY, 5, 5, Random(0L))
|
||||
|
||||
// DOCSTART VaultQueryExample22
|
||||
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
|
||||
val sumCriteria = VaultCustomQueryCriteria(sum)
|
||||
|
||||
val max = builder { CashSchemaV1.PersistentCashState::pennies.max(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
|
||||
val maxCriteria = VaultCustomQueryCriteria(max)
|
||||
|
||||
val min = builder { CashSchemaV1.PersistentCashState::pennies.min(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
|
||||
val minCriteria = VaultCustomQueryCriteria(min)
|
||||
|
||||
val avg = builder { CashSchemaV1.PersistentCashState::pennies.avg(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
|
||||
val avgCriteria = VaultCustomQueryCriteria(avg)
|
||||
|
||||
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(sumCriteria
|
||||
.and(maxCriteria)
|
||||
.and(minCriteria)
|
||||
.and(avgCriteria))
|
||||
// DOCEND VaultQueryExample22
|
||||
|
||||
assertThat(results.otherResults).hasSize(24)
|
||||
/** CHF */
|
||||
assertThat(results.otherResults[0]).isEqualTo(50000L)
|
||||
assertThat(results.otherResults[1]).isEqualTo("CHF")
|
||||
assertThat(results.otherResults[2]).isEqualTo(10274L)
|
||||
assertThat(results.otherResults[3]).isEqualTo("CHF")
|
||||
assertThat(results.otherResults[4]).isEqualTo(9481L)
|
||||
assertThat(results.otherResults[5]).isEqualTo("CHF")
|
||||
assertThat(results.otherResults[6]).isEqualTo(10000.0)
|
||||
assertThat(results.otherResults[7]).isEqualTo("CHF")
|
||||
/** GBP */
|
||||
assertThat(results.otherResults[8]).isEqualTo(40000L)
|
||||
assertThat(results.otherResults[9]).isEqualTo("GBP")
|
||||
assertThat(results.otherResults[10]).isEqualTo(10343L)
|
||||
assertThat(results.otherResults[11]).isEqualTo("GBP")
|
||||
assertThat(results.otherResults[12]).isEqualTo(9351L)
|
||||
assertThat(results.otherResults[13]).isEqualTo("GBP")
|
||||
assertThat(results.otherResults[14]).isEqualTo(10000.0)
|
||||
assertThat(results.otherResults[15]).isEqualTo("GBP")
|
||||
/** USD */
|
||||
assertThat(results.otherResults[16]).isEqualTo(60000L)
|
||||
assertThat(results.otherResults[17]).isEqualTo("USD")
|
||||
assertThat(results.otherResults[18]).isEqualTo(11298L)
|
||||
assertThat(results.otherResults[19]).isEqualTo("USD")
|
||||
assertThat(results.otherResults[20]).isEqualTo(8702L)
|
||||
assertThat(results.otherResults[21]).isEqualTo("USD")
|
||||
assertThat(results.otherResults[22]).isEqualTo(10000.0)
|
||||
assertThat(results.otherResults[23]).isEqualTo("USD")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `aggregate functions sum by issuer and currency and sort by aggregate sum`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER)
|
||||
services.fillWithSomeTestCash(200.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1), issuerKey = BOC_KEY)
|
||||
services.fillWithSomeTestCash(300.POUNDS, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER)
|
||||
services.fillWithSomeTestCash(400.POUNDS, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2), issuerKey = BOC_KEY)
|
||||
|
||||
// DOCSTART VaultQueryExample23
|
||||
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::issuerParty,
|
||||
CashSchemaV1.PersistentCashState::currency),
|
||||
orderBy = Sort.Direction.DESC)
|
||||
}
|
||||
|
||||
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(VaultCustomQueryCriteria(sum))
|
||||
// DOCEND VaultQueryExample23
|
||||
|
||||
assertThat(results.otherResults).hasSize(12)
|
||||
|
||||
assertThat(results.otherResults[0]).isEqualTo(40000L)
|
||||
assertThat(results.otherResults[1]).isEqualTo(BOC_PUBKEY.toBase58String())
|
||||
assertThat(results.otherResults[2]).isEqualTo("GBP")
|
||||
assertThat(results.otherResults[3]).isEqualTo(30000L)
|
||||
assertThat(results.otherResults[4]).isEqualTo(DUMMY_CASH_ISSUER.party.owningKey.toBase58String())
|
||||
assertThat(results.otherResults[5]).isEqualTo("GBP")
|
||||
assertThat(results.otherResults[6]).isEqualTo(20000L)
|
||||
assertThat(results.otherResults[7]).isEqualTo(BOC_PUBKEY.toBase58String())
|
||||
assertThat(results.otherResults[8]).isEqualTo("USD")
|
||||
assertThat(results.otherResults[9]).isEqualTo(10000L)
|
||||
assertThat(results.otherResults[10]).isEqualTo(DUMMY_CASH_ISSUER.party.owningKey.toBase58String())
|
||||
assertThat(results.otherResults[11]).isEqualTo("USD")
|
||||
}
|
||||
}
|
||||
|
||||
private val TODAY = LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC)
|
||||
|
||||
@Test
|
||||
@ -862,7 +996,7 @@ class VaultQueryTests {
|
||||
// DOCSTART VaultQueryExample9
|
||||
val linearStateCriteria = LinearStateQueryCriteria(linearId = listOf(linearId), status = Vault.StateStatus.ALL)
|
||||
val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
val results = vaultQuerySvc.queryBy<LinearState>(linearStateCriteria.and(vaultCriteria))
|
||||
val results = vaultQuerySvc.queryBy<LinearState>(linearStateCriteria and vaultCriteria)
|
||||
// DOCEND VaultQueryExample9
|
||||
assertThat(results.states).hasSize(4)
|
||||
}
|
||||
@ -1176,6 +1310,52 @@ class VaultQueryTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed cash balance for single currency`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(200.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||
|
||||
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
|
||||
val sumCriteria = VaultCustomQueryCriteria(sum)
|
||||
|
||||
val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(USD.currencyCode) }
|
||||
val ccyCriteria = VaultCustomQueryCriteria(ccyIndex)
|
||||
|
||||
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(sumCriteria.and(ccyCriteria))
|
||||
|
||||
assertThat(results.otherResults).hasSize(2)
|
||||
assertThat(results.otherResults[0]).isEqualTo(30000L)
|
||||
assertThat(results.otherResults[1]).isEqualTo("USD")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed cash balances for all currencies`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(200.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||
services.fillWithSomeTestCash(300.POUNDS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(400.POUNDS, DUMMY_NOTARY, 4, 4, Random(0L))
|
||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, DUMMY_NOTARY, 5, 5, Random(0L))
|
||||
services.fillWithSomeTestCash(600.SWISS_FRANCS, DUMMY_NOTARY, 6, 6, Random(0L))
|
||||
|
||||
val ccyIndex = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) }
|
||||
val criteria = VaultCustomQueryCriteria(ccyIndex)
|
||||
val results = vaultQuerySvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
|
||||
assertThat(results.otherResults).hasSize(6)
|
||||
assertThat(results.otherResults[0]).isEqualTo(110000L)
|
||||
assertThat(results.otherResults[1]).isEqualTo("CHF")
|
||||
assertThat(results.otherResults[2]).isEqualTo(70000L)
|
||||
assertThat(results.otherResults[3]).isEqualTo("GBP")
|
||||
assertThat(results.otherResults[4]).isEqualTo(30000L)
|
||||
assertThat(results.otherResults[5]).isEqualTo("USD")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets for quantity greater than`() {
|
||||
database.transaction {
|
||||
|
Reference in New Issue
Block a user