使用pg-mem测试TypeORM的PostGIS geography列触发报错求助
解决pg-mem兼容TypeORM Geography(Point,4326)类型的问题
问题背景
在TypeORM实体中定义了PostGIS地理类型列:
@Index({ spatial: true }) @Column({ type: 'geography', spatialFeatureType: 'Point', srid: 4326, }) geolocation!: Point;
使用pg-mem做单元测试时,触发SQL解析报错:
CREATE TABLE "user" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "geolocation" geography(Point,4326), ...(other columns)
Unexpected word token: "point". Instead, I was expecting to see one of the following:
- A "int" token
此前尝试在PostGIS扩展和public模式下注册多种固定字符串的等效类型,但均未匹配成功。
核心原因
pg-mem的SQL解析器无法识别带参数的geography(Point,4326)类型格式,且固定字符串的等效类型注册无法覆盖动态参数场景(比如大小写差异、空格变化等)。
正确解决方案
通过函数式匹配注册等效类型,覆盖所有geography(...)格式的类型定义,同时确保PostGIS扩展正确加载:
import { DataType, newDb } from 'pg-mem'; import { Point } from 'typeorm'; // 创建pg-mem实例 const db = newDb(); // 注册PostGIS扩展,处理所有geography类型 db.registerExtension('postgis', (schema) => { // 匹配基础geography类型 schema.registerEquivalentType({ name: 'geography', equivalentTo: DataType.point, isValid: (val) => val instanceof Point || val === null || val === undefined, }); // 关键:匹配所有带参数的geography(...)类型(比如geography(Point,4326)) schema.registerEquivalentType({ name: (typeName: string) => typeName.toLowerCase().startsWith('geography('), equivalentTo: DataType.point, isValid: (val) => val instanceof Point || val === null || val === undefined, }); }); // 加载PostGIS扩展到public模式 db.public.none('CREATE EXTENSION postgis'); // 后续连接TypeORM到pg-mem实例即可
补充说明
- 函数式匹配
typeName.toLowerCase().startsWith('geography(')可以忽略大小写和参数差异,覆盖所有地理类型的变体; isValid判断精准匹配TypeORM的Point类型,避免非法值写入;- 必须在创建表之前执行扩展注册和加载操作,确保pg-mem解析SQL时能识别地理类型。
替代方案(自定义类型解析)
如果需要更精准的序列化/解析逻辑,可以直接注册自定义类型:
db.public.registerType({ name: 'geography', oid: 10150, // 自定义唯一OID,避免冲突 parse: (input: string) => { // 解析WKT格式为TypeORM Point对象 const wktMatch = input.match(/^\s*POINT\s*\((\S+)\s+(\S+)\)\s*$/i); if (wktMatch) { return new Point(parseFloat(wktMatch[1]), parseFloat(wktMatch[2])); } return null; }, serialize: (point: Point) => { // 将TypeORM Point序列化为WKT格式 return `POINT(${point.x} ${point.y})`; }, }); // 同时匹配带参数的地理类型 db.public.registerEquivalentType({ name: (name) => name.toLowerCase().includes('geography'), equivalentTo: DataType.point, });
内容的提问来源于stack exchange,提问作者MaxM
相关产品推荐
相关产品推荐

