mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-23 03:29:05 +00:00
Implement block filter for plural queries on subgraph entities (#444)
* Fix bigint values transformation * Fix starts and ends filter operator resolution * Support case insensitive filters for string fields * Add support for global filter _change_block * Handle _change_block filter in all query types
This commit is contained in:
parent
6c17662cad
commit
1b6ca6edeb
@ -27,7 +27,8 @@ import {
|
|||||||
getSubgraphConfig,
|
getSubgraphConfig,
|
||||||
Transaction,
|
Transaction,
|
||||||
EthClient,
|
EthClient,
|
||||||
DEFAULT_LIMIT
|
DEFAULT_LIMIT,
|
||||||
|
FILTER_CHANGE_BLOCK
|
||||||
} from '@cerc-io/util';
|
} from '@cerc-io/util';
|
||||||
|
|
||||||
import { Context, GraphData, instantiate } from './loader';
|
import { Context, GraphData, instantiate } from './loader';
|
||||||
@ -322,6 +323,17 @@ export class GraphWatcher {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
where = Object.entries(where).reduce((acc: { [key: string]: any }, [fieldWithSuffix, value]) => {
|
where = Object.entries(where).reduce((acc: { [key: string]: any }, [fieldWithSuffix, value]) => {
|
||||||
|
if (fieldWithSuffix === FILTER_CHANGE_BLOCK) {
|
||||||
|
assert(value.number_gte && typeof value.number_gte === 'number');
|
||||||
|
|
||||||
|
// Maintain util.Where type
|
||||||
|
acc[FILTER_CHANGE_BLOCK] = [{
|
||||||
|
value: value.number_gte
|
||||||
|
}];
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
const [field, ...suffix] = fieldWithSuffix.split('_');
|
const [field, ...suffix] = fieldWithSuffix.split('_');
|
||||||
|
|
||||||
if (!acc[field]) {
|
if (!acc[field]) {
|
||||||
@ -345,6 +357,11 @@ export class GraphWatcher {
|
|||||||
filter.operator = operator;
|
filter.operator = operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If filter field ends with "nocase", use case insensitive version of the operator
|
||||||
|
if (suffix[suffix.length - 1] === 'nocase') {
|
||||||
|
filter.operator = `${operator}_nocase`;
|
||||||
|
}
|
||||||
|
|
||||||
acc[field].push(filter);
|
acc[field].push(filter);
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
@ -35,7 +35,10 @@ export const OPERATOR_MAP = {
|
|||||||
in: 'IN',
|
in: 'IN',
|
||||||
contains: 'LIKE',
|
contains: 'LIKE',
|
||||||
starts: 'LIKE',
|
starts: 'LIKE',
|
||||||
ends: 'LIKE'
|
ends: 'LIKE',
|
||||||
|
contains_nocase: 'ILIKE',
|
||||||
|
starts_nocase: 'ILIKE',
|
||||||
|
ends_nocase: 'ILIKE'
|
||||||
};
|
};
|
||||||
|
|
||||||
const INSERT_EVENTS_BATCH = 100;
|
const INSERT_EVENTS_BATCH = 100;
|
||||||
@ -875,11 +878,11 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['contains', 'starts'].some(el => el === operator)) {
|
if (['contains', 'contains_nocase', 'ends', 'ends_nocase'].some(el => el === operator)) {
|
||||||
value = `%${value}`;
|
value = `%${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['contains', 'ends'].some(el => el === operator)) {
|
if (['contains', 'contains_nocase', 'starts', 'starts_nocase'].some(el => el === operator)) {
|
||||||
value += '%';
|
value += '%';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,19 +930,22 @@ export class Database {
|
|||||||
eventCount.set(res);
|
eventCount.set(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Transform in the GQL type BigInt parsing itself
|
||||||
_transformBigIntValues (value: any): any {
|
_transformBigIntValues (value: any): any {
|
||||||
|
// Handle array of bigints
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
if (value.length > 0 && typeof value[0] === 'bigint') {
|
if (value.length > 0 && typeof value[0] === 'bigint') {
|
||||||
return value.map(val => {
|
return value.map(val => {
|
||||||
return val.toString();
|
return val.toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle bigint
|
||||||
if (typeof value === 'bigint') {
|
if (typeof value === 'bigint') {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ import { fromStateEntityValues } from './state-utils';
|
|||||||
|
|
||||||
const log = debug('vulcanize:graph-database');
|
const log = debug('vulcanize:graph-database');
|
||||||
|
|
||||||
|
export const FILTER_CHANGE_BLOCK = '_change_block';
|
||||||
|
|
||||||
export const DEFAULT_LIMIT = 100;
|
export const DEFAULT_LIMIT = 100;
|
||||||
const DEFAULT_CLEAR_ENTITIES_CACHE_INTERVAL = 1000;
|
const DEFAULT_CLEAR_ENTITIES_CACHE_INTERVAL = 1000;
|
||||||
|
|
||||||
@ -433,6 +435,11 @@ export class GraphDatabase {
|
|||||||
delete where.id;
|
delete where.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (where[FILTER_CHANGE_BLOCK]) {
|
||||||
|
subQuery = subQuery.andWhere('subTable.block_number >= :changeBlockNumber', { changeBlockNumber: where[FILTER_CHANGE_BLOCK][0].value });
|
||||||
|
delete where[FILTER_CHANGE_BLOCK];
|
||||||
|
}
|
||||||
|
|
||||||
if (block.hash) {
|
if (block.hash) {
|
||||||
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
||||||
|
|
||||||
@ -496,6 +503,11 @@ export class GraphDatabase {
|
|||||||
delete where.id;
|
delete where.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (where[FILTER_CHANGE_BLOCK]) {
|
||||||
|
subQuery = subQuery.andWhere('subTable.block_number >= :changeBlockNumber', { changeBlockNumber: where[FILTER_CHANGE_BLOCK][0].value });
|
||||||
|
delete where[FILTER_CHANGE_BLOCK];
|
||||||
|
}
|
||||||
|
|
||||||
if (block.hash) {
|
if (block.hash) {
|
||||||
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
||||||
|
|
||||||
@ -554,6 +566,11 @@ export class GraphDatabase {
|
|||||||
.addOrderBy(`${tableName}.block_number`, 'DESC')
|
.addOrderBy(`${tableName}.block_number`, 'DESC')
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
|
if (where[FILTER_CHANGE_BLOCK]) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.andWhere(`${tableName}.block_number >= :changeBlockNumber`, { changeBlockNumber: where[FILTER_CHANGE_BLOCK][0].value });
|
||||||
|
delete where[FILTER_CHANGE_BLOCK];
|
||||||
|
}
|
||||||
|
|
||||||
if (block.hash) {
|
if (block.hash) {
|
||||||
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
||||||
|
|
||||||
@ -588,6 +605,11 @@ export class GraphDatabase {
|
|||||||
let selectQueryBuilder = repo.createQueryBuilder(tableName)
|
let selectQueryBuilder = repo.createQueryBuilder(tableName)
|
||||||
.where('is_pruned = :isPruned', { isPruned: false });
|
.where('is_pruned = :isPruned', { isPruned: false });
|
||||||
|
|
||||||
|
if (where[FILTER_CHANGE_BLOCK]) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.andWhere(`${tableName}.block_number >= :changeBlockNumber`, { changeBlockNumber: where[FILTER_CHANGE_BLOCK][0].value });
|
||||||
|
delete where[FILTER_CHANGE_BLOCK];
|
||||||
|
}
|
||||||
|
|
||||||
if (block.hash) {
|
if (block.hash) {
|
||||||
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
||||||
|
|
||||||
@ -658,6 +680,11 @@ export class GraphDatabase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (where[FILTER_CHANGE_BLOCK]) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.andWhere('latest.block_number >= :changeBlockNumber', { changeBlockNumber: where[FILTER_CHANGE_BLOCK][0].value });
|
||||||
|
delete where[FILTER_CHANGE_BLOCK];
|
||||||
|
}
|
||||||
|
|
||||||
selectQueryBuilder = this._baseDatabase.buildQuery(repo, selectQueryBuilder, where, 'latest');
|
selectQueryBuilder = this._baseDatabase.buildQuery(repo, selectQueryBuilder, where, 'latest');
|
||||||
|
|
||||||
if (queryOptions.orderBy) {
|
if (queryOptions.orderBy) {
|
||||||
@ -694,6 +721,11 @@ export class GraphDatabase {
|
|||||||
.orderBy('subTable.block_number', 'DESC')
|
.orderBy('subTable.block_number', 'DESC')
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
|
if (where[FILTER_CHANGE_BLOCK]) {
|
||||||
|
subQuery = subQuery.andWhere('subTable.block_number >= :changeBlockNumber', { changeBlockNumber: where[FILTER_CHANGE_BLOCK][0].value });
|
||||||
|
delete where[FILTER_CHANGE_BLOCK];
|
||||||
|
}
|
||||||
|
|
||||||
if (block.hash) {
|
if (block.hash) {
|
||||||
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
const { canonicalBlockNumber, blockHashes } = await this._baseDatabase.getFrothyRegion(queryRunner, block.hash);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user