mirror of
https://github.com/cerc-io/watcher-ts
synced 2026-09-08 16:54:08 +00:00
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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,7 +18,6 @@ export class RelatedEntity extends Entity {
|
||||
this.set("id", Value.fromString(id));
|
||||
|
||||
this.set("paramBigInt", Value.fromBigInt(BigInt.zero()));
|
||||
this.set("examples", Value.fromStringArray(new Array(0)));
|
||||
this.set("bigIntArray", Value.fromBigIntArray(new Array(0)));
|
||||
}
|
||||
|
||||
@@ -57,15 +56,6 @@ export class RelatedEntity extends Entity {
|
||||
this.set("paramBigInt", Value.fromBigInt(value));
|
||||
}
|
||||
|
||||
get examples(): Array<string> {
|
||||
let value = this.get("examples");
|
||||
return value!.toStringArray();
|
||||
}
|
||||
|
||||
set examples(value: Array<string>) {
|
||||
this.set("examples", Value.fromStringArray(value));
|
||||
}
|
||||
|
||||
get bigIntArray(): Array<BigInt> {
|
||||
let value = this.get("bigIntArray");
|
||||
return value!.toBigIntArray();
|
||||
@@ -89,6 +79,7 @@ export class ExampleEntity extends Entity {
|
||||
this.set("paramEnum", Value.fromString(""));
|
||||
this.set("paramBigDecimal", Value.fromBigDecimal(BigDecimal.zero()));
|
||||
this.set("related", Value.fromString(""));
|
||||
this.set("manyRelated", Value.fromStringArray(new Array(0)));
|
||||
}
|
||||
|
||||
save(): void {
|
||||
@@ -188,4 +179,59 @@ export class ExampleEntity extends Entity {
|
||||
set related(value: string) {
|
||||
this.set("related", Value.fromString(value));
|
||||
}
|
||||
|
||||
get manyRelated(): Array<string> {
|
||||
let value = this.get("manyRelated");
|
||||
return value!.toStringArray();
|
||||
}
|
||||
|
||||
set manyRelated(value: Array<string>) {
|
||||
this.set("manyRelated", Value.fromStringArray(value));
|
||||
}
|
||||
}
|
||||
|
||||
export class ManyRelatedEntity extends Entity {
|
||||
constructor(id: string) {
|
||||
super();
|
||||
this.set("id", Value.fromString(id));
|
||||
|
||||
this.set("count", Value.fromBigInt(BigInt.zero()));
|
||||
}
|
||||
|
||||
save(): void {
|
||||
let id = this.get("id");
|
||||
assert(id != null, "Cannot save ManyRelatedEntity entity without an ID");
|
||||
if (id) {
|
||||
assert(
|
||||
id.kind == ValueKind.STRING,
|
||||
"Cannot save ManyRelatedEntity entity with non-string ID. " +
|
||||
'Considering using .toHex() to convert the "id" to a string.'
|
||||
);
|
||||
store.set("ManyRelatedEntity", id.toString(), this);
|
||||
}
|
||||
}
|
||||
|
||||
static load(id: string): ManyRelatedEntity | null {
|
||||
return changetype<ManyRelatedEntity | null>(
|
||||
store.get("ManyRelatedEntity", id)
|
||||
);
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
let value = this.get("id");
|
||||
return value!.toString();
|
||||
}
|
||||
|
||||
set id(value: string) {
|
||||
this.set("id", Value.fromString(value));
|
||||
}
|
||||
|
||||
get count(): BigInt {
|
||||
let value = this.get("count");
|
||||
return value!.toBigInt();
|
||||
}
|
||||
|
||||
set count(value: BigInt) {
|
||||
this.set("count", Value.fromBigInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ enum EnumType {
|
||||
type RelatedEntity @entity {
|
||||
id: ID!
|
||||
paramBigInt: BigInt!
|
||||
examples: [ExampleEntity!]!
|
||||
bigIntArray: [BigInt!]!
|
||||
}
|
||||
|
||||
@@ -20,4 +19,10 @@ type ExampleEntity @entity {
|
||||
paramEnum: EnumType!
|
||||
paramBigDecimal: BigDecimal!
|
||||
related: RelatedEntity!
|
||||
manyRelated: [ManyRelatedEntity!]!
|
||||
}
|
||||
|
||||
type ManyRelatedEntity @entity {
|
||||
id: ID!
|
||||
count: BigInt!
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
Example1,
|
||||
Test
|
||||
} from '../generated/Example1/Example1';
|
||||
import { ExampleEntity, RelatedEntity } from '../generated/schema';
|
||||
import { ExampleEntity, ManyRelatedEntity, RelatedEntity } from '../generated/schema';
|
||||
|
||||
export function handleTest (event: Test): void {
|
||||
log.debug('event.address: {}', [event.address.toHexString()]);
|
||||
@@ -15,12 +15,12 @@ export function handleTest (event: Test): void {
|
||||
|
||||
// Entities can be loaded from the store using a string ID; this ID
|
||||
// needs to be unique across all entities of the same type
|
||||
let entity = ExampleEntity.load(event.transaction.hash.toHexString());
|
||||
let entity = ExampleEntity.load(event.transaction.from.toHex());
|
||||
|
||||
// Entities only exist after they have been saved to the store;
|
||||
// `null` checks allow to create entities on demand
|
||||
if (!entity) {
|
||||
entity = new ExampleEntity(event.transaction.hash.toHexString());
|
||||
entity = new ExampleEntity(event.transaction.from.toHex());
|
||||
|
||||
// Entity fields can be set using simple assignments
|
||||
entity.count = BigInt.fromString('0');
|
||||
@@ -37,10 +37,10 @@ export function handleTest (event: Test): void {
|
||||
entity.paramEnum = 'choice1';
|
||||
entity.paramBigDecimal = BigDecimal.fromString('123');
|
||||
|
||||
let relatedEntity = RelatedEntity.load(event.transaction.from.toHex());
|
||||
let relatedEntity = RelatedEntity.load(event.params.param1);
|
||||
|
||||
if (!relatedEntity) {
|
||||
relatedEntity = new RelatedEntity(event.transaction.from.toHex());
|
||||
relatedEntity = new RelatedEntity(event.params.param1);
|
||||
relatedEntity.paramBigInt = BigInt.fromString('123');
|
||||
}
|
||||
|
||||
@@ -48,14 +48,17 @@ export function handleTest (event: Test): void {
|
||||
bigIntArray.push(entity.count);
|
||||
relatedEntity.bigIntArray = bigIntArray;
|
||||
|
||||
const examples = relatedEntity.examples;
|
||||
examples.push(entity.id);
|
||||
relatedEntity.examples = examples;
|
||||
|
||||
relatedEntity.save();
|
||||
|
||||
entity.related = relatedEntity.id;
|
||||
|
||||
const manyRelatedEntity = new ManyRelatedEntity(event.transaction.hash.toHexString());
|
||||
manyRelatedEntity.count = entity.count;
|
||||
manyRelatedEntity.save();
|
||||
|
||||
const manyRelated = entity.manyRelated;
|
||||
manyRelated.push(manyRelatedEntity.id);
|
||||
entity.manyRelated = manyRelated;
|
||||
|
||||
// Entities can be written to the store with `.save()`
|
||||
entity.save();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user