Implement gql queries for relation entities similar to subgraph (#53)

* Add implementation for one to one relation

* Implement one to many relation in gql queries

* Make changes for gql relation queries in eden-watcher

* Implement subgraph gql relation queries with joins
This commit is contained in:
2021-12-28 16:08:05 +05:30
committed by nabarun
parent 44b3fd59e8
commit f52467f724
13 changed files with 320 additions and 34 deletions
+59
View File
@@ -73,6 +73,65 @@ export class Database {
}
}
async getEntityWithRelations<Entity> (entity: (new () => Entity) | string, id: string, blockHash: string, relations: { [key: string]: any }): Promise<Entity | undefined> {
const queryRunner = this._conn.createQueryRunner();
try {
const repo = queryRunner.manager.getRepository(entity);
// Fetching blockHash for previous entity in frothy region.
const { blockHash: entityblockHash, blockNumber, id: frothyId } = await this._baseDatabase.getFrothyEntity(queryRunner, repo, { blockHash, id });
let selectQueryBuilder = repo.createQueryBuilder('entity');
if (frothyId) {
// If entity found in frothy region.
selectQueryBuilder = selectQueryBuilder.where('entity.block_hash = :entityblockHash', { entityblockHash });
} else {
// If entity not in frothy region.
const canonicalBlockNumber = blockNumber + 1;
selectQueryBuilder = selectQueryBuilder.innerJoinAndSelect('block_progress', 'block', 'block.block_hash = entity.block_hash')
.where('block.is_pruned = false')
.andWhere('entity.block_number <= :canonicalBlockNumber', { canonicalBlockNumber })
.orderBy('entity.block_number', 'DESC')
.limit(1);
}
selectQueryBuilder = selectQueryBuilder.andWhere('entity.id = :id', { id });
// TODO: Implement query for nested relations.
Object.entries(relations).forEach(([field, data], index) => {
const { entity: relatedEntity, isArray } = data;
const alias = `relatedEntity${index}`;
if (isArray) {
// For one to many relational field.
selectQueryBuilder = selectQueryBuilder.leftJoinAndMapMany(
`entity.${field}`,
relatedEntity,
alias,
`${alias}.id IN (SELECT unnest(entity.${field})) AND ${alias}.block_number <= entity.block_number`
)
.addOrderBy(`${alias}.block_number`, 'DESC');
} else {
// For one to one relational field.
selectQueryBuilder = selectQueryBuilder.leftJoinAndMapOne(
`entity.${field}`,
relatedEntity,
alias,
`entity.${field} = ${alias}.id AND ${alias}.block_number <= entity.block_number`
)
.addOrderBy(`${alias}.block_number`, 'DESC');
}
});
return selectQueryBuilder.getOne();
} finally {
await queryRunner.release();
}
}
async saveEntity (entity: string, data: any): Promise<void> {
const repo = this._conn.getRepository(entity);
+2 -2
View File
@@ -184,9 +184,9 @@ export class GraphWatcher {
this._indexer = indexer;
}
async getEntity<Entity> (entity: new () => Entity, id: string, blockHash: string): Promise<any> {
async getEntity<Entity> (entity: new () => Entity, id: string, blockHash: string, relations: { [key: string]: any }): Promise<any> {
// Get entity from the database.
const result = await this._database.getEntity(entity, id, blockHash) as any;
const result = await this._database.getEntityWithRelations(entity, id, blockHash, relations) as any;
// Resolve any field name conflicts in the entity result.
return resolveEntityFieldConflicts(result);