diff --git a/packages/uni-info-watcher/src/database.ts b/packages/uni-info-watcher/src/database.ts index 2e2d4c4b..2b7128f1 100644 --- a/packages/uni-info-watcher/src/database.ts +++ b/packages/uni-info-watcher/src/database.ts @@ -62,11 +62,11 @@ export interface QueryOptions { } interface Where { - [key: string]: { + [key: string]: [{ value: any; not: boolean; operator: keyof typeof OPERATOR_MAP; - } + }] } export class Database { @@ -479,40 +479,43 @@ export class Database { selectQueryBuilder = selectQueryBuilder.leftJoinAndSelect(`${repo.metadata.tableName}.${relation}`, relation); }); - Object.entries(where).forEach(([field, filter]) => { - // Form the where clause. - const { not, operator, value } = filter; - const columnMetadata = repo.metadata.findColumnWithPropertyName(field); - assert(columnMetadata); - let whereClause = `${tableName}.${columnMetadata.propertyAliasName} `; + Object.entries(where).forEach(([field, filters]) => { + filters.forEach((filter, index) => { + // Form the where clause. + const { not, operator, value } = filter; + const columnMetadata = repo.metadata.findColumnWithPropertyName(field); + assert(columnMetadata); + let whereClause = `${tableName}.${columnMetadata.propertyAliasName} `; - if (not) { - if (operator === 'equals') { - whereClause += '!'; - } else { - whereClause += 'NOT '; + if (not) { + if (operator === 'equals') { + whereClause += '!'; + } else { + whereClause += 'NOT '; + } } - } - whereClause += `${OPERATOR_MAP[operator]} `; + whereClause += `${OPERATOR_MAP[operator]} `; - if (['contains', 'starts'].some(el => el === operator)) { - whereClause += '%:'; - } else if (operator === 'in') { - whereClause += '(:...'; - } else { - whereClause += ':'; - } + if (['contains', 'starts'].some(el => el === operator)) { + whereClause += '%:'; + } else if (operator === 'in') { + whereClause += '(:...'; + } else { + whereClause += ':'; + } - whereClause += 'value'; + const variableName = `${field}${index}`; + whereClause += variableName; - if (['contains', 'ends'].some(el => el === operator)) { - whereClause += '%'; - } else if (operator === 'in') { - whereClause += ')'; - } + if (['contains', 'ends'].some(el => el === operator)) { + whereClause += '%'; + } else if (operator === 'in') { + whereClause += ')'; + } - selectQueryBuilder = selectQueryBuilder.andWhere(whereClause, { value }); + selectQueryBuilder = selectQueryBuilder.andWhere(whereClause, { [variableName]: value }); + }); }); const { limit = DEFAULT_LIMIT, orderBy, orderDirection, skip = DEFAULT_SKIP } = queryOptions; diff --git a/packages/uni-info-watcher/src/indexer.ts b/packages/uni-info-watcher/src/indexer.ts index 163e5911..51d33c54 100644 --- a/packages/uni-info-watcher/src/indexer.ts +++ b/packages/uni-info-watcher/src/indexer.ts @@ -206,6 +206,30 @@ export class Indexer { return block; } + async getBlocks (where: { [key: string]: any } = {}, queryOptions: QueryOptions): Promise { + if (where.timestamp_gt) { + where.blockTimestamp_gt = where.timestamp_gt; + delete where.timestamp_gt; + } + + if (where.timestamp_lt) { + where.blockTimestamp_lt = where.timestamp_lt; + delete where.timestamp_lt; + } + + if (queryOptions.orderBy === 'timestamp') { + queryOptions.orderBy = 'blockTimestamp'; + } + + const blocks = await this.getEntities(BlockProgress, {}, where, queryOptions); + + return blocks.map(block => ({ + timestamp: block.blockTimestamp, + number: block.blockNumber, + hash: block.blockHash + })); + } + async getEvent (id: string): Promise { return this._db.getEvent(id); } @@ -290,7 +314,11 @@ export class Indexer { where = Object.entries(where).reduce((acc: { [key: string]: any }, [fieldWithSuffix, value]) => { const [field, ...suffix] = fieldWithSuffix.split('_'); - acc[field] = { + if (!acc[field]) { + acc[field] = []; + } + + const filter = { value, not: false, operator: 'equals' @@ -299,14 +327,16 @@ export class Indexer { let operator = suffix.shift(); if (operator === 'not') { - acc[field].not = true; + filter.not = true; operator = suffix.shift(); } if (operator) { - acc[field].operator = operator; + filter.operator = operator; } + acc[field].push(filter); + return acc; }, {}); diff --git a/packages/uni-info-watcher/src/resolvers.ts b/packages/uni-info-watcher/src/resolvers.ts index d17058c1..763d294a 100644 --- a/packages/uni-info-watcher/src/resolvers.ts +++ b/packages/uni-info-watcher/src/resolvers.ts @@ -133,6 +133,12 @@ export const createResolvers = async (indexer: Indexer): Promise => { log('positions', first, where); return indexer.getEntities(Position, {}, where, { limit: first }, ['pool', 'token0', 'token1', 'tickLower', 'tickUpper', 'transaction']); + }, + + blocks: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => { + log('blocks', first, orderBy, orderDirection, where); + + return indexer.getBlocks(where, { limit: first, orderBy, orderDirection }); } } }; diff --git a/packages/uni-info-watcher/src/schema.ts b/packages/uni-info-watcher/src/schema.ts index 52c02bf1..2635a70c 100644 --- a/packages/uni-info-watcher/src/schema.ts +++ b/packages/uni-info-watcher/src/schema.ts @@ -160,6 +160,12 @@ type Position { feeGrowthInside1LastX128: BigInt! } +type Block { + number: Int! + hash: Bytes! + timestamp: Int! +} + enum OrderDirection { asc desc @@ -268,6 +274,15 @@ input Position_filter { id: ID } +input Block_filter { + timestamp_gt: Int + timestamp_lt: Int +} + +enum Block_orderBy { + timestamp +} + type Query { bundle( id: ID! @@ -413,5 +428,12 @@ type Query { first: Int = 100 where: Position_filter ): [Position!]! + + blocks( + first: Int = 100 + orderBy: Block_orderBy + orderDirection: OrderDirection + where: Block_filter + ): [Block!]! } `;