Legacy documentation: You are viewing version CURRENT; the latest available is LATEST.

Go to latest →

Queries

Introduction

Data structures in the Chain Core API are represented as key-value JSON objects. This includes local objects, such as accounts and keys, and global objects, such as transactions and assets. To retrieve data, you perform a query with optional parameters. By default, each query returns a time-ordered list of objects beginning with the most recent.

Filters

Filters allow narrowing results to those matching a set of supplied parameters.

A filter is composed of one or more terms, with multiple terms joined with AND and OR. Each term contains a property, operator, and value. Each term targets a specific field in the key-value (JSON) object (see API Objects). Terms can be grouped together in a scope to target a specific array of sub-objects within an object.

For example, to list transactions where a specific account spends a specific asset, you would create a filter with two terms, scoped to the inputs:

inputs(account_alias='alice' AND asset_alias='gold')

Properties

Any field in a JSON object can be used as a filter property. To use a field that is nested within another field, provide the path to it, starting with the outermost parent object. For example:

asset_definition.issuer.name

Note: although you can create asset definitions, tags, and reference data with any valid JSON object, you can only query fieldnames that contain letters, numbers, and underscores.

Operators

Filters currently support only the = operator, which allows you to search for exact matches of string and integer values. Other data types, such as booleans, are not supported.

There are two methods of providing search values to the = operator. First, you can include them inline, surrounded by single quotes:

alias='alice'

Alternatively, you can specify a parameterized filter, without single quotes:

alias=$1 OR alias=$2

When using parameterized filters, you should also provide an ordered set of values for the parameters:

["Bob's account", "Bob's dog's account"]

The SDK supports both parameterized and non-parameterized filters. The dashboard does not support parameterized filters.

Scope

The transaction object contains an array of other objects: an inputs array and an outputs array. The inputs() and outputs() filter scopes allow targeting a specific object within those arrays.

For example, the following will return transactions where Alice sent gold to Bob:

inputs(account_alias='alice' AND asset_alias='gold') AND outputs(account_alias='bob' AND asset_alias='gold')

Additional parameters

Transaction queries accept time parameters to limit the results within a time window.

Method Description
setStartTime Sets the earliest transaction timestamp to include in results.
setEndTime Sets the latest transaction timestamp to include in results.

Balance and unspent output queries accept a timestamp parameter to report ownership at a specific moment in time.

Method Description
setTimestamp Sets a timestamp at which to calculate balances or return unspent outputs.

Special Case: Balance queries

Any balance on the blockchain is simply a summation of unspent outputs. For example, the balance of Alice’s account is a summation of all the unspent outputs whose control programs were created from the keys in Alice’s account.

Unlike other queries in Chain Core, balance queries do not return Chain Core objects, only simple sums over the amount fields in a specified list of unspent output objects.

Sum By

Balance sums are totalled by asset_id and asset_alias by default, but it is also possible to query more complex sums. For example, if you have a network of counterparty-issued IOUs, you may wish to calculate the account balance of all IOUs from different counterparties that represent the same underlying currency.

Overview

This guide will walk you through several examples of queries:

Sample Code

All code samples in this guide can be viewed in a single, runnable script. Available languages:

Transactions

List all transactions involving Alice’s account:

Transaction.Items aliceTransactions = new Transaction.QueryBuilder()
  .setFilter("inputs(account_alias=$1) OR outputs(account_alias=$1)")
  .addFilterParameter("alice")
  .execute(client);

while (aliceTransactions.hasNext()) {
  Transaction transaction = aliceTransactions.next();
  System.out.println("Alice's transaction " + transaction.id);
  for (Transaction.Input input: transaction.inputs) {
    if (input.accountAlias != null) {
      System.out.println("  -" + input.amount + " " + input.assetAlias);
    }
  }
  for (Transaction.Output output: transaction.outputs) {
    if (output.accountAlias != null) {
      System.out.println("  +" + output.amount + " " + output.assetAlias);
    }
  }
}
alice_txs = chain.transactions.query(
  filter: 'inputs(account_alias=$1) OR outputs(account_alias=$1)',
  filter_params: ['alice'],
).each do |tx|
  puts "Alice's transaction: #{tx.id}"

  tx.inputs.each do |input|
    puts "-#{input.amount} #{input.asset_alias}"
  end

  tx.outputs.each do |output|
    puts "+#{output.amount} #{output.asset_alias}"
  end
end
client.transactions.queryAll({
  filter: 'inputs(account_alias=$1) OR outputs(account_alias=$1)',
  filterParams: ['alice'],
}, (tx, next, done) => {
  console.log("Alice's transaction: " + tx.id)

  tx.inputs.forEach(input => {
    console.log('-' + input.amount + ' ' + input.assetAlias)
  })

  tx.outputs.forEach(output => {
    console.log('+' + output.amount + ' ' + output.assetAlias)
  })

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

List all transactions involving accounts with checking as the type in the account tags:

Transaction.Items checkingTransactions = new Transaction.QueryBuilder()
  .setFilter("inputs(account_tags.type=$1) OR outputs(account_tags.type=$1)")
  .addFilterParameter("checking")
  .execute(client);

while (checkingTransactions.hasNext()) {
  Transaction transaction = checkingTransactions.next();
  System.out.println("Checking account transaction " + transaction.id);
  for (Transaction.Input input: transaction.inputs) {
    if (input.accountAlias != null) {
      System.out.println("  -" + input.amount + " " + input.assetAlias);
    }
  }
  for (Transaction.Output output: transaction.outputs) {
    if (output.accountAlias != null) {
      System.out.println("  +" + output.amount + " " + output.assetAlias);
    }
  }
}
checkings_txs = chain.transactions.query(
  filter: 'inputs(account_tags.type=$1) OR outputs(account_tags.type=$1)',
  filter_params: ['checking'],
).each do |tx|
  puts "Checking account transaction: #{tx.id}"

  tx.inputs.each do |input|
    puts "-#{input.amount} #{input.asset_alias}"
  end

  tx.outputs.each do |output|
    puts "+#{output.amount} #{output.asset_alias}"
  end
end
client.transactions.queryAll({
  filter: 'inputs(account_tags.type=$1) OR outputs(account_tags.type=$1)',
  filterParams: ['checking'],
}, (tx, next, done) => {
  console.log("Checking account transaction: " + tx.id)

  tx.inputs.forEach(input => {
    console.log('-' + input.amount + ' ' + input.assetAlias)
  })

  tx.outputs.forEach(output => {
    console.log('+' + output.amount + ' ' + output.assetAlias)
  })

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

List all transactions involving the local Core:

Transaction.Items localTransactions = new Transaction.QueryBuilder()
  .setFilter("is_local=$1")
  .addFilterParameter("yes")
  .execute(client);

while (localTransactions.hasNext()) {
  Transaction transaction = localTransactions.next();
  System.out.println("Local transaction " + transaction.id);
}
chain.transactions.query(
  filter: 'is_local=$1',
  filter_params: ['yes'],
).each do |tx|
  puts "Local transaction #{tx.id}"
end
client.transactions.queryAll({
  filter: 'is_local=$1',
  filterParams: ['yes']
}, (tx, next, done) => {
  console.log('Local transaction ' + tx.id)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

Transaction queries and tag updates

You can query for transactions using asset and account tags. These queries use an internal index that is generated using the values of asset and account tags at the time each transaction was indexed.

Asset and account tags can be updated, but these updates are not retroactively applied to the transaction query index. Transactions that are indexed after a tag update can be queried by the new value of the tags, but transactions indexed before the tag update must be queried by the old value.

Assets

List all assets created in the local Core:

Asset.Items localAssets = new Asset.QueryBuilder()
  .setFilter("is_local=$1")
  .addFilterParameter("yes")
  .execute(client);

while (localAssets.hasNext()) {
  Asset asset = localAssets.next();
  System.out.println("Local asset " + asset.id + " (" + asset.alias + ")");
}
chain.assets.query(
  filter: 'is_local=$1',
  filter_params: ['yes'],
).each do |asset|
  puts "Local asset #{asset.id} (#{asset.alias})"
end
client.assets.queryAll({
  filter: 'is_local=$1',
  filterParams: ['yes']
}, (asset, next, done) => {
  console.log('Local asset ' + asset.id + ' (' + asset.alias + ')')

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

List all assets with USD as the currency in the asset definition:

Asset.Items usdAssets = new Asset.QueryBuilder()
  .setFilter("definition.currency=$1")
  .addFilterParameter("USD")
  .execute(client);

while (usdAssets.hasNext()) {
  Asset asset = usdAssets.next();
  System.out.println("USD asset " + asset.id + " (" + asset.alias + ")");
}
chain.assets.query(
  filter: 'definition.currency=$1',
  filter_params: ['USD'],
).each do |asset|
  puts "USD asset #{asset.id} (#{asset.alias})"
end
client.assets.queryAll({
  filter: 'definition.currency=$1',
  filterParams: ['USD']
}, (asset, next, done) => {
  console.log('USD asset ' + asset.id + ' (' + asset.alias + ')')

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

After updating the value of an asset’s tag, you can query for that asset using the new value of the tag.

Accounts

List all accounts with checking as the type in the account tags:

Account.Items checkingAccounts = new Account.QueryBuilder()
  .setFilter("tags.type=$1")
  .addFilterParameter("checking")
  .execute(client);

while (checkingAccounts.hasNext()) {
  Account account = checkingAccounts.next();
  System.out.println("Checking account " + account.id + " (" + account.alias + ")");
}
chain.accounts.query(
  filter: 'tags.type=$1',
  filter_params: ['checking'],
).each do |account|
  puts "Checking account #{account.id} (#{account.alias})"
end
client.accounts.queryAll({
  filter: 'tags.type=$1',
  filterParams: ['checking']
}, (account, next, done) => {
  console.log('Checking account ' + account.id + ' (' + account.alias + ')')

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

After updating the value of an account’s tag, you can query for that account using the new value of the tag.

Unspent Outputs

List all unspent outputs controlled by Alice’s account:

UnspentOutput.Items aliceUnspentOuputs = new UnspentOutput.QueryBuilder()
  .setFilter("account_alias=$1")
  .addFilterParameter("alice")
  .execute(client);

while (aliceUnspentOuputs.hasNext()) {
  UnspentOutput utxo = aliceUnspentOuputs.next();
  System.out.println("Alice's unspent output: " + utxo.amount + " " + utxo.assetAlias);
}
chain.unspent_outputs.query(
  filter: 'account_alias=$1',
  filter_params: ['alice'],
).each do |utxo|
  puts "Alice's unspent output: #{utxo.amount} #{utxo.asset_alias}"
end
client.unspentOutputs.queryAll({
  filter: 'account_alias=$1',
  filterParams: ['alice']
}, (utxo, next, done) => {
  console.log("Alice's unspent output: " + utxo.amount + ' ' + utxo.assetAlias)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

List all unspent outputs with checking as the type in the account tags:

UnspentOutput.Items checkingUnspentOuputs = new UnspentOutput.QueryBuilder()
  .setFilter("account_tags.type=$1")
  .addFilterParameter("checking")
  .execute(client);

while (checkingUnspentOuputs.hasNext()) {
  UnspentOutput utxo = checkingUnspentOuputs.next();
  System.out.println("Checking account unspent output: " + utxo.amount + " " + utxo.assetAlias);
}
chain.unspent_outputs.query(
  filter: 'account_tags.type=$1',
  filter_params: ['checking'],
).each do |utxo|
  puts "Checking account unspent output: #{utxo.amount} #{utxo.asset_alias}"
end
client.unspentOutputs.queryAll({
  filter: 'account_tags.type=$1',
  filterParams: ['checking']
}, (utxo, next, done) => {
  console.log("Checking account unspent output: " + utxo.amount + ' ' + utxo.assetAlias)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

Unspent queries and tag updates

You can query for unspent outputs using asset and account tags. These queries use an internal index that is generated using the values of asset and account tags at the time each unspent output was indexed.

Asset and account tags can be updated, but these updates are not retroactively applied to the unspent output query index. Unspent outputs that are indexed after a tag update can be queried by the new value of the tags, but unspent outputs indexed before the tag update must be queried by the old value.

Balances

List the asset IOU balances in Bank1’s account:

Balance.Items bank1Balances = new Balance.QueryBuilder()
  .setFilter("account_alias=$1")
  .addFilterParameter("bank1")
  .execute(client);

while (bank1Balances.hasNext()) {
  Balance b = bank1Balances.next();
  System.out.println(
    "Bank 1 balance of " + b.sumBy.get("asset_alias") +
    ": " + b.amount
  );
}
chain.balances.query(
  filter: 'account_alias=$1',
  filter_params: ['bank1'],
).each do |b|
  puts "Bank 1 balance of #{b.sum_by['asset_alias']}: #{b.amount}"
end
client.balances.queryAll({
  filter: 'account_alias=$1',
  filterParams: ['bank1']
}, (balance, next, done) => {
  console.log('Bank 1 balance of ' + balance.sumBy.assetAlias + ': ' + balance.amount)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

Get the circulation of the Bank 1 USD IOU on the blockchain:

Balance.Items bank1UsdIouBalances = new Balance.QueryBuilder()
  .setFilter("asset_alias=$1")
  .addFilterParameter("bank1_usd_iou")
  .execute(client);

Balance bank1UsdIouCirculation = bank1UsdIouBalances.next();
System.out.println("Total circulation of Bank 1 USD IOU: " + bank1UsdIouCirculation.amount);
circulation = chain.balances.query(
  filter: 'asset_alias=$1',
  filter_params: ['bank1_usd_iou'],
).first

puts("Total circulation of Bank 1 USD IOU:  #{circulation.amount}")
client.balances.queryAll({
  filter: 'asset_alias=$1',
  filterParams: ['bank1UsdIou']
}, (balance, next, done) => {
  console.log('Total circulation of Bank 1 USD IOU: ' + balance.amount)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

List the asset IOU balances in Bank1’s account, summed by currency:

Balance.Items bank1CurrencyBalances = new Balance.QueryBuilder()
  .setFilter("account_alias=$1")
  .addFilterParameter("bank1")
  .setSumBy(Arrays.asList("asset_definition.currency"))
  .execute(client);

while (bank1CurrencyBalances.hasNext()) {
  Balance b = bank1CurrencyBalances.next();
  System.out.println(
    "Bank 1 balance of " + b.sumBy.get("asset_definition.currency") +
    "-denominated currencies : " + b.amount
  );
}
chain.balances.query(
  filter: 'account_alias=$1',
  filter_params: ['bank1'],
  sum_by: ['asset_definition.currency']
).each do |b|
  denom = b.sum_by['asset_definition.currency']
  puts "Bank 1 balance of #{denom}-denominated currencies: #{b.amount}"
end
client.balances.queryAll({
  filter: 'account_alias=$1',
  filterParams: ['bank1'],
  sumBy: ['asset_definition.currency']
}, (balance, next, done) => {
  var denom = balance.sumBy['assetDefinition.currency']
  console.log('Bank 1 balance of ' + denom + '-denominated currencies: ' + balance.amount)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

List balances of accounts with checking as the type in the account tags, summed by currency:

Balance.Items checkingBalances = new Balance.QueryBuilder()
  .setFilter("account_tags.type=$1")
  .addFilterParameter("checking")
  .execute(client);

while (checkingBalances.hasNext()) {
  Balance b = checkingBalances.next();
  System.out.println(
    "Checking account balance of " + b.sumBy.get("asset_alias") +
    ": " + b.amount
  );
}
chain.balances.query(
  filter: 'account_tags.type=$1',
  filter_params: ['checking'],
).each do |b|
  puts "Checking accounts balance of #{b.sum_by['asset_alias']}: #{b.amount}"
end
client.balances.queryAll({
  filter: 'account_tags.type=$1',
  filterParams: ['checking']
}, (balance, next, done) => {
  console.log('Checking accounts balance of ' + balance.sumBy.assetAlias + ': ' + balance.amount)

  // next() moves to the next item.
  // done() terminates the loop early, and causes the
  //   query promise to resolve. Passing an error will reject
  //   the promise.
  next()
})
  • Java
  • Node
  • Ruby

Balances and tag updates

You can query for balances using asset and account tags. These queries use the unspent output index, which is generated using the values of asset and account tags at the time each unspent output was indexed.

Asset and account tags can be updated, but these updates are not retroactively applied to the unspent output query index. Balance queries that rely on the new value of a tag will only reflect unspent outputs that were indexed after the tag was updated.