From 4e6626f27e3dbfb908b5d9b64ce25dfb007947f7 Mon Sep 17 00:00:00 2001 From: mogbonjubolaolasunkanmi-art Date: Mon, 22 Jun 2026 08:42:52 +0000 Subject: [PATCH] fix(gists): dynamic radius with ST_DWithin, distance in response (#607) - Add GIST spatial index on location::geography for query performance - Add ::geography cast to ST_DWithin and ST_Distance in findNearby() - Add expires_at > NOW() filter to findNearby() - Sort results by distance_meters ASC, created_at DESC - Map distance_meters -> distanceMeters on each returned Gist - Add distanceMeters optional field to Gist entity type - Fix duplicate import and unreachable code in gists.service.ts Closes #607 --- Backend/src/gists/entities/gist.entity.ts | 4 ++++ Backend/src/gists/gist.repository.ts | 14 ++++++++++---- Backend/src/gists/gists.service.ts | 13 +------------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Backend/src/gists/entities/gist.entity.ts b/Backend/src/gists/entities/gist.entity.ts index 8eae7db4..6a27fc8b 100644 --- a/Backend/src/gists/entities/gist.entity.ts +++ b/Backend/src/gists/entities/gist.entity.ts @@ -3,6 +3,7 @@ import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, Index } from @Entity('gists') @Index('idx_gists_location_cell') @Index('idx_gists_author_address') +@Index('idx_gists_location_geography', { synchronize: false }) export class Gist { @PrimaryGeneratedColumn('uuid') id: string; @@ -36,4 +37,7 @@ export class Gist { @CreateDateColumn({ type: 'timestamptz' }) created_at: Date; + + /** Populated by findNearby() — not a persisted column. */ + distanceMeters?: number; } diff --git a/Backend/src/gists/gist.repository.ts b/Backend/src/gists/gist.repository.ts index 2bf69cc6..46893621 100644 --- a/Backend/src/gists/gist.repository.ts +++ b/Backend/src/gists/gist.repository.ts @@ -96,7 +96,7 @@ export class GistRepository { const extraWhere = clauses.length > 0 ? `AND ${clauses.join(' AND ')}` : ''; - const items = await this.dataSource.query( + const rows = await this.dataSource.query>( ` SELECT g.id, @@ -110,22 +110,28 @@ export class GistRepository { ST_X(g.location::geometry) AS lon, ST_Y(g.location::geometry) AS lat, ST_Distance( - g.location, + g.location::geography, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography ) AS distance_meters FROM gists g WHERE ST_DWithin( - g.location, + g.location::geography, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography, $3 ) + AND g.expires_at > NOW() ${extraWhere} - ORDER BY g.created_at DESC + ORDER BY distance_meters ASC, g.created_at DESC LIMIT $4 `, params, ); + const items: Gist[] = rows.map((r) => ({ + ...r, + distanceMeters: parseFloat(r.distance_meters), + })); + return PaginationHelper.buildResponse(items, limit); } diff --git a/Backend/src/gists/gists.service.ts b/Backend/src/gists/gists.service.ts index edae0882..8812604a 100644 --- a/Backend/src/gists/gists.service.ts +++ b/Backend/src/gists/gists.service.ts @@ -1,7 +1,6 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { CreateGistDto } from './dto/create-gist.dto'; import { QueryGistsDto } from './dto/query-gists.dto'; import { GistRepository, PG_UNIQUE_VIOLATION } from './gist.repository'; @@ -86,16 +85,6 @@ export class GistsService { } throw err; } - return this.gistRepository.create({ - content, - lat: dto.lat, - lon: dto.lon, - location_cell: locationCell, - content_hash: cid, - stellar_gist_id: gistId, - tx_hash: txHash, - author_address: dto.author, - }); } async findNearby(query: QueryGistsDto): Promise> {