Support filters for array and derived type fields in plural GQL queries (#448)

* Handle nested filters for array type fields

* Transform Decimal param values to strings for db queries

* Handle nested filters for derived fields

* Handle filters for array type fields
This commit is contained in:
prathamesh0 2023-11-03 12:14:32 +05:30 committed by GitHub
parent 92f3fb8252
commit 7f37dac888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -22,6 +22,7 @@ import {
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import _ from 'lodash'; import _ from 'lodash';
import { Pool } from 'pg'; import { Pool } from 'pg';
import Decimal from 'decimal.js';
import { BlockProgressInterface, ContractInterface, EventInterface, StateInterface, StateSyncStatusInterface, StateKind, SyncStatusInterface } from './types'; import { BlockProgressInterface, ContractInterface, EventInterface, StateInterface, StateSyncStatusInterface, StateKind, SyncStatusInterface } from './types';
import { MAX_REORG_DEPTH, UNKNOWN_EVENT_NAME } from './constants'; import { MAX_REORG_DEPTH, UNKNOWN_EVENT_NAME } from './constants';
@ -839,21 +840,32 @@ export class Database {
} }
Object.entries(where).forEach(([field, filters]) => { Object.entries(where).forEach(([field, filters]) => {
// TODO: Handle nested filters on derived and array fields
const columnMetadata = repo.metadata.findColumnWithPropertyName(field); const columnMetadata = repo.metadata.findColumnWithPropertyName(field);
assert(columnMetadata);
filters.forEach((filter, index) => { filters.forEach((filter, index) => {
let { not, operator, value } = filter; let { not, operator, value } = filter;
// Handle nested relation filter
const relation = relations[field]; const relation = relations[field];
// Handle nested relation filter (only one level deep supported)
if (operator === 'nested' && relation) { if (operator === 'nested' && relation) {
const relationRepo = this.conn.getRepository<any>(relation.entity); const relationRepo = this.conn.getRepository<any>(relation.entity);
const relationTableName = relationRepo.metadata.tableName; const relationTableName = relationRepo.metadata.tableName;
let relationSubQuery: SelectQueryBuilder<any> = relationRepo.createQueryBuilder(relationTableName, repo.queryRunner) let relationSubQuery: SelectQueryBuilder<any> = relationRepo.createQueryBuilder(relationTableName, repo.queryRunner)
.select('1') .select('1');
.where(`${relationTableName}.id = "${alias}"."${columnMetadata.databaseName}"`);
if (relation.isDerived) {
const derivationField = relation.field;
relationSubQuery = relationSubQuery.where(`${relationTableName}.${derivationField} = ${alias}.id`);
} else {
// Column has to exist for non-derived fields
assert(columnMetadata);
if (relation.isArray) {
relationSubQuery = relationSubQuery.where(`${relationTableName}.id = ANY(${alias}.${columnMetadata.databaseName})`);
} else {
relationSubQuery = relationSubQuery.where(`${relationTableName}.id = ${alias}.${columnMetadata.databaseName}`);
}
}
// canonicalBlockHashes take precedence over block number if provided // canonicalBlockHashes take precedence over block number if provided
if (block.canonicalBlockHashes) { if (block.canonicalBlockHashes) {
@ -874,15 +886,27 @@ export class Database {
return; return;
} }
// Form the where clause. // Column has to exist if it's not a nested filter
let whereClause = `"${alias}"."${columnMetadata.databaseName}" `; assert(columnMetadata);
const columnIsArray = columnMetadata.isArray;
if (columnMetadata.relationMetadata) { // Form the where clause.
// For relation fields, use the id column. assert(operator);
const idColumn = columnMetadata.relationMetadata.joinColumns.find(column => column.referencedColumn?.propertyName === 'id'); let whereClause = '';
assert(idColumn);
whereClause = `"${alias}"."${idColumn.databaseName}" `; // In case of array field having contains, NOT comes before the field name
// Disregards nocase
if (columnIsArray && operator.includes('contains')) {
if (not) {
whereClause += 'NOT ';
whereClause += `"${alias}"."${columnMetadata.databaseName}" `;
whereClause += '&& ';
} else {
whereClause += `"${alias}"."${columnMetadata.databaseName}" `;
whereClause += '@> ';
} }
} else {
whereClause += `"${alias}"."${columnMetadata.databaseName}" `;
if (not) { if (not) {
if (operator === 'equals') { if (operator === 'equals') {
@ -892,10 +916,10 @@ export class Database {
} }
} }
assert(operator);
whereClause += `${OPERATOR_MAP[operator]} `; whereClause += `${OPERATOR_MAP[operator]} `;
}
value = this._transformBigIntValues(value); value = this._transformBigValues(value);
if (operator === 'in') { if (operator === 'in') {
whereClause += '(:...'; whereClause += '(:...';
} else { } else {
@ -913,6 +937,7 @@ export class Database {
} }
} }
if (!columnIsArray) {
if (['contains', 'contains_nocase', 'ends', 'ends_nocase'].some(el => el === operator)) { if (['contains', 'contains_nocase', 'ends', 'ends_nocase'].some(el => el === operator)) {
value = `%${value}`; value = `%${value}`;
} }
@ -920,6 +945,7 @@ export class Database {
if (['contains', 'contains_nocase', 'starts', 'starts_nocase'].some(el => el === operator)) { if (['contains', 'contains_nocase', 'starts', 'starts_nocase'].some(el => el === operator)) {
value += '%'; value += '%';
} }
}
selectQueryBuilder = selectQueryBuilder.andWhere(whereClause, { [variableName]: value }); selectQueryBuilder = selectQueryBuilder.andWhere(whereClause, { [variableName]: value });
}); });
@ -966,10 +992,10 @@ export class Database {
} }
// TODO: Transform in the GQL type BigInt parsing itself // TODO: Transform in the GQL type BigInt parsing itself
_transformBigIntValues (value: any): any { _transformBigValues (value: any): any {
// Handle array of bigints // 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' || Decimal.isDecimal(value[0]))) {
return value.map(val => { return value.map(val => {
return val.toString(); return val.toString();
}); });
@ -977,7 +1003,7 @@ export class Database {
} }
// Handle bigint // Handle bigint
if (typeof value === 'bigint') { if (typeof value === 'bigint' || Decimal.isDecimal(value)) {
return value.toString(); return value.toString();
} }