Package: conseil
ConseilPyPython toolkit forConseilblockchain indexerInstallationPython 3.6+ required$pipinstallconseilUsageConseilPy is a lot like Sqlalchemy, so if you're familiar with it, you can easily cook queries.QuickstartGet top 5 delegators by balancefromconseilimportconseilAccount=conseil.tezos.alphanet.accountsAccount.query(Account.acccount_id,Account.balance)\.filter(Account.script.is_(None),Account.account_id.startswith('KT1'))\.order_by(Account.balance.desc())\.limit(5)\.all()See moreexamplesClient initializationIf using a default conseil client is not an option you can instantiate it yourself:fromconseil.apiimportConseilApifromconseil.coreimportClientconseil=Client(ConseilApi(api_key='<API_KEY>',api_host='<API_HOST>',api_version=2))Exploring database schemaConseil metadata has the following tree structure:platform / network / entity / attribute / valueSo you can simply access any node by name:>>>fromconseilimportconseil>>>print(conseil.tezos.alphanet.operations.kind.transaction)transactionAutocompletionShift + Taband docstrings are available in Jupyter:>>>conseilPathmetadata/platformsPlatforms.tezos>>>conseil.tezos.alphanetPathmetadata/tezos/alphanet/entitiesEntities.accounts.balance_updates.ballots.blocks.delegates.fees.operation_groups.operations.proposals.rollsAlternatively you can check fullSQL schemaSelecting fieldsConseil doesn't support joins at the moment so you can request attributes for a single entity only.fromconseilimportconseilc=conseil.tezos.alphanet# select all fieldsc.query(c.accounts)c.accounts.query()# select specific fieldsc.query(c.accounts.balance,c.accounts.account_id)c.accounts.query(c.accounts.balance,c.accounts.account_id)# select single fieldc.accounts.balance.query()Filtering resultsConseil receives a conjunction of predicates, which can be inverted by one, but not together.
Predicate syntax is similar to Sqlalchemy, but has less operations.fromconseilimportconseilfromconseil.coreimportnot_Account=conseil.tezos.alphanet.accountsAccount.query()\.filter(not_(Account.account_id.startswith('tz')),Account.script.is_(None),Account.balance>0)Here is a full list of supported operations:Conseil operationFilterInversedinx.in_(a, b, ...)x.notin_(a, b, ...)betweenx.between(a, b)not_(x.between(a, b))likex.like(a)x.notlike(a)ltx < ax >= agtx > ax <= aeqx == ax != astartsWithx.startswith(a)not_(x.startsWith(a))endsWithx.endswith(a)not_(x.endswith(a))isnullx.is_(None)x.isnot(None)You can also usefilter_byfor simple queries:fromconseilimportconseilconseil.tezos.alphanet.accounts.query()\.filter_by(account_id='tzkt')Data aggregationThis is an important concept to understand. In Conseil you specify which columns will be aggregated and the rest of them are used inGROUP BYclause. Here is an example:fromconseilimportconseilBlock=conseil.tezos.alphanet.blocksBlock.query(Block.baker,Block.level.count(),Block.timestamp.max())# will be compiled to SELECT baker, COUNT(level), MAX(timestamp) FROM blocks GROUP BY bakerAdditionally, you can specifyHAVINGpredicates if you want to filter results by aggregated column:fromconseilimportconseilBlock=conseil.tezos.alphanet.blocksBlock.query(Block.baker,Block.level.count())\.having(Block.level.count()>1)# you have to specify aggregation function here as wellHere is the list of supported aggregation functions:countsumavgminmaxIf you want to group by some fields but not include them in the result usegroup_bymethod:fromconseilimportconseilBlock=conseil.tezos.alphanet.blocksBlock.query(Block.level.count())\.group_by(Block.baker)Sorting and limiting resultsThis is similar to Sqlalchemy as well, you can specify one or multiple sort columns with optional descending modifier.fromconseilimportconseilAccount=conseil.tezos.alphanet.accountsAccount.query()\.order_by(Account.balance.desc(),Account.account_id)\.limit(20)You can sort by aggregated column too:fromconseilimportconseilOperation=conseil.tezos.alphanet.operationsOperation.query(Operation.source,Operation.amount.avg())\.order_by(Operation.amount.avg().desc())\.limit(50)Query previewSo you have cooked a simple query and want to see the resulting Conseil request body.fromconseilimportconseilAccount=conseil.tezos.alphanet.accountsquery=Account.query()\.order_by(Account.balance.desc())\.limit(1)Then you can simply:>>>queryPathdata/tezos/alphanet/accountsQuery{"aggregation":[],"fields":[],"limit":1,"orderBy":[{"direction":"desc","field":"balance"}],"output":"json","predicates":[]}ExecutionIt's time to submit our query and get some data.fromconseilimportconseilAccount=conseil.tezos.alphanet.accountsReturn multiple rowsquery=Account.query()query.all()# will return List[dict] (default output type)query.all(output='csv')# will return string (csv)Return single rowquery=Account.query()\.filter_by(account_id='tzkt')query.one()# will fail if there is no account with such id or there are manyquery.one_or_none()# will handle the exception and return NoneReturn scalarquery=Account.balance.query()\.order_by(Account.balance.desc())\.limit(1)query.scalar()# will return single numeric valueReturn vectorquery=Operation.query(Operation.timestamp)\.filter_by(source='tzkt')query.vector()# will return flat list of timestampsPrecisionConseil allows to specify numeric column precision. In order to use this functionality usedecimaltype. For example:fromconseilimportconseilfromdecimalimportDecimalAccount=conseil.tezos.alphanet.accountsAccount.query(Account.balance)\.filter(Account.balance>Decimal('0.1'),Account.balance<Decimal('0.01'))# precision will be 2 (max)Renaming fieldsYou can change names of requested fields in the resulting json/csv:fromconseilimportconseilAccount=conseil.tezos.alphanet.accountsAccount.query(Account.account_id.label('address'))