Support nested filters for plural queries on subgraph entities (#446)

* Handle nested filters while building the database query

* Build nested filters for relational fields

* Avoid queries to get canonical block hashes for loading relations

* Handle nested filters in all query types

* Give precedence to block hash over number in time travel queries
This commit is contained in:
prathamesh0
2023-11-02 10:37:33 +05:30
committed by GitHub
parent 9fb51e89f6
commit 92f3fb8252
3 changed files with 170 additions and 134 deletions
+64 -45
View File
@@ -28,7 +28,10 @@ import {
Transaction,
EthClient,
DEFAULT_LIMIT,
FILTER_CHANGE_BLOCK
FILTER_CHANGE_BLOCK,
Where,
Filter,
OPERATOR_MAP
} from '@cerc-io/util';
import { Context, GraphData, instantiate } from './loader';
@@ -322,50 +325,7 @@ export class GraphWatcher {
const dbTx = await this._database.createTransactionRunner();
try {
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('_');
if (!acc[field]) {
acc[field] = [];
}
const filter = {
value,
not: false,
operator: 'equals'
};
let operator = suffix.shift();
if (operator === 'not') {
filter.not = true;
operator = suffix.shift();
}
if (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);
return acc;
}, {});
where = this._buildFilter(where);
if (!queryOptions.limit) {
queryOptions.limit = DEFAULT_LIMIT;
@@ -498,6 +458,65 @@ export class GraphWatcher {
return transaction;
}
_buildFilter (where: { [key: string]: any } = {}): Where {
return Object.entries(where).reduce((acc: Where, [fieldWithSuffix, value]) => {
if (fieldWithSuffix === FILTER_CHANGE_BLOCK) {
assert(value.number_gte && typeof value.number_gte === 'number');
acc[FILTER_CHANGE_BLOCK] = [{
value: value.number_gte,
not: false
}];
return acc;
}
const [field, ...suffix] = fieldWithSuffix.split('_');
if (!acc[field]) {
acc[field] = [];
}
const filter: Filter = {
value,
not: false,
operator: 'equals'
};
let operator = suffix.shift();
// If the operator is "" (different from undefined), it means it's a nested filter on a relation field
if (operator === '') {
acc[field].push({
// Parse nested filter value
value: this._buildFilter(value),
not: false,
operator: 'nested'
});
return acc;
}
if (operator === 'not') {
filter.not = true;
operator = suffix.shift();
}
if (operator) {
filter.operator = operator as keyof typeof OPERATOR_MAP;
}
// If filter field ends with "nocase", use case insensitive version of the operator
if (suffix[suffix.length - 1] === 'nocase') {
filter.operator = `${operator}_nocase` as keyof typeof OPERATOR_MAP;
}
acc[field].push(filter);
return acc;
}, {});
}
}
export const getGraphDbAndWatcher = async (