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,
|
||||
} 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')
|
||||
@Index('idx_knowledge_articles_category', ['category'])
|
||||
@Index('idx_knowledge_articles_published', ['isPublished'])
|
||||
|
|
@ -35,7 +51,11 @@ export class KnowledgeArticleORM {
|
|||
@Column({ name: 'source_url', length: 1000, nullable: true })
|
||||
sourceUrl?: string;
|
||||
|
||||
@Column('float', { array: true, nullable: true })
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
transformer: vectorTransformer,
|
||||
})
|
||||
embedding?: number[];
|
||||
|
||||
@Column({ name: 'is_published', default: false })
|
||||
|
|
|
|||
|
|
@ -6,6 +6,22 @@ import {
|
|||
Index,
|
||||
} 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')
|
||||
@Index('idx_knowledge_chunks_article', ['articleId'])
|
||||
export class KnowledgeChunkORM {
|
||||
|
|
@ -24,7 +40,11 @@ export class KnowledgeChunkORM {
|
|||
@Column({ name: 'chunk_type', length: 20 })
|
||||
chunkType: string;
|
||||
|
||||
@Column('float', { array: true, nullable: true })
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
transformer: vectorTransformer,
|
||||
})
|
||||
embedding?: number[];
|
||||
|
||||
@Column('jsonb', { default: '{}' })
|
||||
|
|
|
|||
|
|
@ -7,6 +7,22 @@ import {
|
|||
Index,
|
||||
} 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')
|
||||
@Index('idx_system_experiences_type', ['experienceType'])
|
||||
@Index('idx_system_experiences_status', ['verificationStatus'])
|
||||
|
|
@ -51,7 +67,11 @@ export class SystemExperienceORM {
|
|||
@Column({ name: 'negative_count', default: 0 })
|
||||
negativeCount: number;
|
||||
|
||||
@Column('float', { array: true, nullable: true })
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
transformer: vectorTransformer,
|
||||
})
|
||||
embedding?: number[];
|
||||
|
||||
@Column({ name: 'is_active', default: false })
|
||||
|
|
|
|||
|
|
@ -7,6 +7,23 @@ import {
|
|||
Index,
|
||||
} 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')
|
||||
@Index('idx_user_memories_user', ['userId'])
|
||||
@Index('idx_user_memories_type', ['memoryType'])
|
||||
|
|
@ -32,7 +49,11 @@ export class UserMemoryORM {
|
|||
@Column({ name: 'related_category', length: 50, nullable: true })
|
||||
relatedCategory?: string;
|
||||
|
||||
@Column('float', { array: true, nullable: true })
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
transformer: vectorTransformer,
|
||||
})
|
||||
embedding?: number[];
|
||||
|
||||
@Column({ name: 'access_count', default: 0 })
|
||||
|
|
|
|||
Loading…
Reference in New Issue