PG Vector Filter Expression 문제
배경
- Filter expression을 사용해서 vector store에서 값을 지우려고 시도 했음
- pg vector filter expression을 parsing을 못 한다고 함
[DELETE FROM public.vector_store WHERE metadata::jsonb @@ '($.ragDocumentId == [41])'::jsonpath]
in연상자를 사용했는데==로 파싱된 것이 이상함
List<Long> ragDocumentIds = targets.stream().map(RagDocument::getId).toList();
FilterExpressionBuilder b = new FilterExpressionBuilder();
Filter.Expression expression = b.in("ragDocumentId", ragDocumentIds).build();
vectorStore.delete(expression);
2026-01-05T12:26:01.559+09:00 ERROR 27876 --- [personal-website] [ scheduling-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task
java.lang.IllegalStateException: Failed to delete documents by filter
at org.springframework.ai.vectorstore.pgvector.PgVectorStore.doDelete(PgVectorStore.java:356) ~[spring-ai-pgvector-store-1.1.2.jar:1.1.2]
at
...
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [DELETE FROM public.vector_store WHERE metadata::jsonb @@ '($.ragDocumentId == [41])'::jsonpath]
...
org.springframework.ai.vectorstore.pgvector.PgVectorStore.doDelete(PgVectorStore.java:353) ~[spring-ai-pgvector-store-1.1.2.jar:1.1.2]
... 29 common frames omitted
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "[" of jsonpath input
Position: 58
at
...
문제 분석
Pg Vector를 위한 filter expression 를 SQL query로 바꿔주는 class
- PG의 json에서는 in 연산자이 없어서 and, or를 사용한 query로 변환
- 파싱 될 때,
List<Long> ragDocumentIds가 list가 아닌 Object로 인식 되어서[41]로 한번에 사용됨
// PgVectorFilterExpressionConverter
@Override
protected void doExpression(Expression expression, StringBuilder context) {
if (expression.type() == Filter.ExpressionType.IN) {
handleIn(expression, context);
}
...
private void handleIn(Expression expression, StringBuilder context) {
context.append("(");
convertToConditions(expression, context);
context.append(")");
}
private void convertToConditions(Expression expression, StringBuilder context) {
...
List<Object> values = (List) value;
for (int i = 0; i < values.size(); i++) {
this.convertOperand(expression.left(), context);
context.append(" == ");
this.doSingleValue(values.get(i), context);
if (i < values.size() - 1) {
context.append(" || ");
}
}
}
- in 연산자 overloading에서 method가 잘못 mapping 된 것이 문제
- List
// FilterExpressionBuilder
public Op in(String key, Object... values) {
return this.in(key, List.of(values));
}
public Op in(String key, List<Object> values) {
return new Op(new Filter.Expression(ExpressionType.IN, new Key(key), new Value(values)));
}
해결
- Object로 인식되는 문제를 없애기 위해 array로 넘김
Long[] ragDocumentIds = targets.stream().map(RagDocument::getId).toArray();