fix(knowledge): add pgvector transformer for TypeORM embedding columns
TypeORM doesn't natively support pgvector type. Add custom transformer to convert between JavaScript arrays and pgvector string format [1,2,3]. Fixes: invalid input syntax for type vector errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
71b98c2d07
commit
ad0f904f98
|
|
@ -7,6 +7,22 @@ import {
|
||||||
Index,
|
Index,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pgvector 类型转换器
|
||||||
|
*/
|
||||||
|
const vectorTransformer = {
|
||||||
|
to: (value: number[] | undefined): string | null => {
|
||||||
|
if (!value || value.length === 0) return null;
|
||||||
|
return `[${value.join(',')}]`;
|
||||||
|
},
|
||||||
|
from: (value: string | null): number[] | undefined => {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const str = value.replace(/^\[|\]$/g, '');
|
||||||
|
if (!str) return undefined;
|
||||||
|
return str.split(',').map(Number);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@Entity('knowledge_articles')
|
@Entity('knowledge_articles')
|
||||||
@Index('idx_knowledge_articles_category', ['category'])
|
@Index('idx_knowledge_articles_category', ['category'])
|
||||||
@Index('idx_knowledge_articles_published', ['isPublished'])
|
@Index('idx_knowledge_articles_published', ['isPublished'])
|
||||||
|
|
@ -35,7 +51,11 @@ export class KnowledgeArticleORM {
|
||||||
@Column({ name: 'source_url', length: 1000, nullable: true })
|
@Column({ name: 'source_url', length: 1000, nullable: true })
|
||||||
sourceUrl?: string;
|
sourceUrl?: string;
|
||||||
|
|
||||||
@Column('float', { array: true, nullable: true })
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
nullable: true,
|
||||||
|
transformer: vectorTransformer,
|
||||||
|
})
|
||||||
embedding?: number[];
|
embedding?: number[];
|
||||||
|
|
||||||
@Column({ name: 'is_published', default: false })
|
@Column({ name: 'is_published', default: false })
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,22 @@ import {
|
||||||
Index,
|
Index,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pgvector 类型转换器
|
||||||
|
*/
|
||||||
|
const vectorTransformer = {
|
||||||
|
to: (value: number[] | undefined): string | null => {
|
||||||
|
if (!value || value.length === 0) return null;
|
||||||
|
return `[${value.join(',')}]`;
|
||||||
|
},
|
||||||
|
from: (value: string | null): number[] | undefined => {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const str = value.replace(/^\[|\]$/g, '');
|
||||||
|
if (!str) return undefined;
|
||||||
|
return str.split(',').map(Number);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@Entity('knowledge_chunks')
|
@Entity('knowledge_chunks')
|
||||||
@Index('idx_knowledge_chunks_article', ['articleId'])
|
@Index('idx_knowledge_chunks_article', ['articleId'])
|
||||||
export class KnowledgeChunkORM {
|
export class KnowledgeChunkORM {
|
||||||
|
|
@ -24,7 +40,11 @@ export class KnowledgeChunkORM {
|
||||||
@Column({ name: 'chunk_type', length: 20 })
|
@Column({ name: 'chunk_type', length: 20 })
|
||||||
chunkType: string;
|
chunkType: string;
|
||||||
|
|
||||||
@Column('float', { array: true, nullable: true })
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
nullable: true,
|
||||||
|
transformer: vectorTransformer,
|
||||||
|
})
|
||||||
embedding?: number[];
|
embedding?: number[];
|
||||||
|
|
||||||
@Column('jsonb', { default: '{}' })
|
@Column('jsonb', { default: '{}' })
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,22 @@ import {
|
||||||
Index,
|
Index,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pgvector 类型转换器
|
||||||
|
*/
|
||||||
|
const vectorTransformer = {
|
||||||
|
to: (value: number[] | undefined): string | null => {
|
||||||
|
if (!value || value.length === 0) return null;
|
||||||
|
return `[${value.join(',')}]`;
|
||||||
|
},
|
||||||
|
from: (value: string | null): number[] | undefined => {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const str = value.replace(/^\[|\]$/g, '');
|
||||||
|
if (!str) return undefined;
|
||||||
|
return str.split(',').map(Number);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@Entity('system_experiences')
|
@Entity('system_experiences')
|
||||||
@Index('idx_system_experiences_type', ['experienceType'])
|
@Index('idx_system_experiences_type', ['experienceType'])
|
||||||
@Index('idx_system_experiences_status', ['verificationStatus'])
|
@Index('idx_system_experiences_status', ['verificationStatus'])
|
||||||
|
|
@ -51,7 +67,11 @@ export class SystemExperienceORM {
|
||||||
@Column({ name: 'negative_count', default: 0 })
|
@Column({ name: 'negative_count', default: 0 })
|
||||||
negativeCount: number;
|
negativeCount: number;
|
||||||
|
|
||||||
@Column('float', { array: true, nullable: true })
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
nullable: true,
|
||||||
|
transformer: vectorTransformer,
|
||||||
|
})
|
||||||
embedding?: number[];
|
embedding?: number[];
|
||||||
|
|
||||||
@Column({ name: 'is_active', default: false })
|
@Column({ name: 'is_active', default: false })
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,23 @@ import {
|
||||||
Index,
|
Index,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pgvector 类型转换器
|
||||||
|
* TypeORM 不原生支持 pgvector,需要手动转换格式
|
||||||
|
*/
|
||||||
|
const vectorTransformer = {
|
||||||
|
to: (value: number[] | undefined): string | null => {
|
||||||
|
if (!value || value.length === 0) return null;
|
||||||
|
return `[${value.join(',')}]`;
|
||||||
|
},
|
||||||
|
from: (value: string | null): number[] | undefined => {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const str = value.replace(/^\[|\]$/g, '');
|
||||||
|
if (!str) return undefined;
|
||||||
|
return str.split(',').map(Number);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@Entity('user_memories')
|
@Entity('user_memories')
|
||||||
@Index('idx_user_memories_user', ['userId'])
|
@Index('idx_user_memories_user', ['userId'])
|
||||||
@Index('idx_user_memories_type', ['memoryType'])
|
@Index('idx_user_memories_type', ['memoryType'])
|
||||||
|
|
@ -32,7 +49,11 @@ export class UserMemoryORM {
|
||||||
@Column({ name: 'related_category', length: 50, nullable: true })
|
@Column({ name: 'related_category', length: 50, nullable: true })
|
||||||
relatedCategory?: string;
|
relatedCategory?: string;
|
||||||
|
|
||||||
@Column('float', { array: true, nullable: true })
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
nullable: true,
|
||||||
|
transformer: vectorTransformer,
|
||||||
|
})
|
||||||
embedding?: number[];
|
embedding?: number[];
|
||||||
|
|
||||||
@Column({ name: 'access_count', default: 0 })
|
@Column({ name: 'access_count', default: 0 })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue