Skip to content

Commit

Permalink
feat: tx list function name fuzzy search
Browse files Browse the repository at this point in the history
  • Loading branch information
He1DAr committed Oct 3, 2024
1 parent 66e6800 commit f663d97
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
19 changes: 19 additions & 0 deletions migrations/1727186561690_idx-contract-call-function-name-trgm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
exports.up = pgm => {
pgm.sql('CREATE EXTENSION IF NOT EXISTS pg_trgm;');

pgm.sql(`
CREATE INDEX idx_contract_call_function_name_trgm
ON txs
USING gin (contract_call_function_name gin_trgm_ops);
`);
};

/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
exports.down = pgm => {
pgm.sql(`
DROP INDEX IF EXISTS idx_contract_call_function_name_trgm;
`);

pgm.sql('DROP EXTENSION IF EXISTS pg_trgm;');
};
12 changes: 12 additions & 0 deletions src/api/routes/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ export const TxRoutes: FastifyPluginAsync<
examples: [1706745599],
})
),
search_term: Type.Optional(
Type.String({
description: 'Option to search for transactions by a search term',
examples: ['swap'],
})
),
contract_id: Type.Optional(
Type.String({
description: 'Option to filter results by contract ID',
Expand Down Expand Up @@ -178,6 +184,11 @@ export const TxRoutes: FastifyPluginAsync<
contractId = req.query.contract_id;
}

let searchTerm: string | undefined;
if (typeof req.query.search_term === 'string') {
searchTerm = req.query.search_term;
}

const { results: txResults, total } = await fastify.db.getTxList({
offset,
limit,
Expand All @@ -188,6 +199,7 @@ export const TxRoutes: FastifyPluginAsync<
startTime: req.query.start_time,
endTime: req.query.end_time,
contractId,
searchTerm,
functionName: req.query.function_name,
nonce: req.query.nonce,
order: req.query.order,
Expand Down
11 changes: 11 additions & 0 deletions src/datastore/pg-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ import { parseBlockParam } from '../api/routes/v2/schemas';

export const MIGRATIONS_DIR = path.join(REPO_DIR, 'migrations');

const TRGM_SIMILARITY_THRESHOLD = 0.3;

/**
* This is the main interface between the API and the Postgres database. It contains all methods that
* query the DB in search for blockchain data to be returned via endpoints or WebSockets/Socket.IO.
Expand Down Expand Up @@ -1416,6 +1418,7 @@ export class PgStore extends BasePgStore {
startTime,
endTime,
contractId,
searchTerm,
functionName,
nonce,
order,
Expand All @@ -1430,6 +1433,7 @@ export class PgStore extends BasePgStore {
startTime?: number;
endTime?: number;
contractId?: string;
searchTerm?: string;
functionName?: string;
nonce?: number;
order?: 'desc' | 'asc';
Expand Down Expand Up @@ -1468,6 +1472,10 @@ export class PgStore extends BasePgStore {
const contractIdFilterSql = contractId
? sql`AND contract_call_contract_id = ${contractId}`
: sql``;

const searchTermFilterSql = searchTerm
? sql`AND similarity(contract_call_function_name, ${searchTerm}) > ${TRGM_SIMILARITY_THRESHOLD}`
: sql``;
const contractFuncFilterSql = functionName
? sql`AND contract_call_function_name = ${functionName}`
: sql``;
Expand All @@ -1479,6 +1487,7 @@ export class PgStore extends BasePgStore {
!startTime &&
!endTime &&
!contractId &&
!searchTerm &&
!functionName &&
!nonce;

Expand All @@ -1497,6 +1506,7 @@ export class PgStore extends BasePgStore {
${startTimeFilterSql}
${endTimeFilterSql}
${contractIdFilterSql}
${searchTermFilterSql}
${contractFuncFilterSql}
${nonceFilterSql}
`;
Expand All @@ -1511,6 +1521,7 @@ export class PgStore extends BasePgStore {
${startTimeFilterSql}
${endTimeFilterSql}
${contractIdFilterSql}
${searchTermFilterSql}
${contractFuncFilterSql}
${nonceFilterSql}
${orderBySql}
Expand Down
70 changes: 70 additions & 0 deletions tests/api/tx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2322,6 +2322,76 @@ describe('tx tests', () => {
);
});

test('tx list - filter by searchTerm using trigram', async () => {
const transferTokenTx = {
tx_id: '0x1111',
contract_call_function_name: 'transferToken',
};

const stakeTokenTx = {
tx_id: '0x2222',
contract_call_function_name: 'stakeToken',
};

const burnTokenTx = {
tx_id: '0x3333',
contract_call_function_name: 'burnToken',
}

const block1 = new TestBlockBuilder({ block_height: 1, index_block_hash: '0x01' })
.addTx(transferTokenTx)
.build();
await db.update(block1);

const block2 = new TestBlockBuilder({
block_height: 2,
index_block_hash: '0x02',
parent_block_hash: block1.block.block_hash,
parent_index_block_hash: block1.block.index_block_hash,
})
.addTx(stakeTokenTx)
.addTx(burnTokenTx)
.build();
await db.update(block2);

const searchTerm = 'transfer';

const txsReq = await supertest(api.server).get(`/extended/v1/tx?search_term=${searchTerm}`);
expect(txsReq.status).toBe(200);
expect(txsReq.body).toEqual(
expect.objectContaining({
results: [
expect.objectContaining({
tx_id: transferTokenTx.tx_id,
}),
],
})
);

const broadSearchTerm = 'token';

const txsReqBroad = await supertest(api.server).get(
`/extended/v1/tx?search_term=${broadSearchTerm}`
);
expect(txsReqBroad.status).toBe(200);

expect(txsReqBroad.body).toEqual(
expect.objectContaining({
results: [
expect.objectContaining({
tx_id: burnTokenTx.tx_id,
}),
expect.objectContaining({
tx_id: stakeTokenTx.tx_id,
}),
expect.objectContaining({
tx_id: transferTokenTx.tx_id,
}),
],
})
);
});

test('tx list - filter by contract id/name', async () => {
const testContractAddr = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world';
const testContractFnName = 'test-contract-fn';
Expand Down

0 comments on commit f663d97

Please sign in to comment.