@arkiv-network/sdk
Version:
TypeScript client library for Arkiv's blockchains interactions
67 lines (64 loc) • 2.54 kB
text/typescript
import { isSchema, type Schema, SchemaDefinitionError } from "../schema"
import { QueryBuilderBase } from "./builderBase"
import { runQuery } from "./engine"
/**
* Builds and runs a count: how many entities a query matches, without fetching any of them.
*
* It filters exactly as `client.select()` does — {@link from}, `where`, `ownedBy` and `createdBy`
* are the same methods, from the same base — but has nothing to select and no pages, so
* {@link fetch} resolves to a `bigint`. Create one with `client.count()`.
*
* To show the total next to a page of rows, use `select(...).withCount()` instead, which counts in
* the same request.
*
* @typeParam TSchema - The schema named with {@link from}, if any.
*
* @example
* const total = await client.count().from(Posts).ownedBy(account.address).fetch()
*/
export class CountQueryBuilder<
TSchema extends Schema | undefined = undefined,
> extends QueryBuilderBase<TSchema> {
/**
* Counts through a schema: `where` is checked against its indexed columns, and a named schema
* counts only entities tagged with its name — so it needs no other filter.
*
* Returns a new builder, carrying whatever this one already holds; this one is left as it was.
*
* @throws {SchemaDefinitionError} If `schema` was not built with `defineSchema`.
*
* @example
* client.count().from(Posts) // every post
*/
from<S extends Schema>(schema: S): CountQueryBuilder<S> {
if (!isSchema(schema)) {
throw new SchemaDefinitionError("from() takes a schema built with defineSchema(...)")
}
const typed = this.copy<CountQueryBuilder<S>>()
typed._schema = schema
return typed
}
/**
* Runs the count.
*
* Counts every entity the filters match at the block, whether or not it fits the schema — what
* `select().from(schema)` leaves out of its pages is still counted.
*
* @throws {InvalidPredicateError} If no filter has been added, or a filter does not fit the
* schema.
* @throws {QueryError} If the node rejects the query.
* @throws If the node returns no count — it predates the count projection.
*/
async fetch(): Promise<bigint> {
// Nothing but the count is selected, so the node returns no rows.
const response = await runQuery(this._client, {
query: this.toString(),
select: {},
count: true,
})
if (response.count === undefined) {
throw new Error("The node returned no count. It may predate the count projection.")
}
return response.count
}
}