Skip to content

Commit 82cbaa7

Browse files
committed
Fix escaping issue in filterExpression for RedisVectorStore file name filtering
Signed-off-by: Minu Kim <kmw106933@naver.com>
1 parent 5b55d59 commit 82cbaa7

File tree

1 file changed

+20
-10
lines changed

1 file changed

+20
-10
lines changed

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SearchRequest.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@
1717
package org.springframework.ai.vectorstore;
1818

1919
import java.util.Objects;
20+
import java.util.Map;
2021

2122
import org.springframework.ai.document.Document;
2223
import org.springframework.ai.vectorstore.filter.Filter;
2324
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
2425
import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser;
2526
import org.springframework.lang.Nullable;
2627
import org.springframework.util.Assert;
27-
import java.util.regex.Pattern;
28-
import java.util.regex.Matcher;
2928

3029
/**
3130
* Similarity search request. Use the {@link SearchRequest#builder()} to create the
@@ -61,6 +60,8 @@ public class SearchRequest {
6160
@Nullable
6261
private Filter.Expression filterExpression;
6362

63+
private static final Map<Character, String> ESCAPE_TEXT = Map.of('\\', "\\\\", '.', "\\.");
64+
6465
/**
6566
* Copy an existing {@link SearchRequest.Builder} instance.
6667
* @param originalSearchRequest {@link SearchRequest} instance to copy.
@@ -193,7 +194,6 @@ public Builder similarityThresholdAll() {
193194
/**
194195
* Retrieves documents by query embedding similarity and matching the filters.
195196
* Value of 'null' means that no metadata filters will be applied to the search.
196-
*
197197
* For example if the {@link Document#getMetadata()} schema is:
198198
*
199199
* <pre>{@code
@@ -290,14 +290,24 @@ public Builder filterExpression(@Nullable String textExpression) {
290290
}
291291

292292
private String escapeTextExpression(String expression) {
293-
Pattern pattern = Pattern.compile("'([^']*)'");
294-
Matcher matcher = pattern.matcher(expression);
295-
StringBuffer sb = new StringBuffer();
296-
while (matcher.find()) {
297-
String content = matcher.group(1).replace("\\", "\\\\").replace(".", "\\.");
298-
matcher.appendReplacement(sb, "'" + content + "'");
293+
StringBuilder sb = new StringBuilder(expression.length() + 8);
294+
boolean inQuote = false;
295+
296+
for (int i = 0; i < expression.length(); i++) {
297+
char ch = expression.charAt(i);
298+
299+
if (ch == '\'') {
300+
inQuote = !inQuote;
301+
sb.append(ch);
302+
}
303+
else if (inQuote) {
304+
sb.append(ESCAPE_TEXT.getOrDefault(ch, String.valueOf(ch)));
305+
}
306+
else {
307+
sb.append(ch);
308+
}
299309
}
300-
matcher.appendTail(sb);
310+
301311
return sb.toString();
302312
}
303313

0 commit comments

Comments
 (0)