|
17 | 17 | package org.springframework.ai.vectorstore;
|
18 | 18 |
|
19 | 19 | import java.util.Objects;
|
| 20 | +import java.util.Map; |
20 | 21 |
|
21 | 22 | import org.springframework.ai.document.Document;
|
22 | 23 | import org.springframework.ai.vectorstore.filter.Filter;
|
23 | 24 | import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
|
24 | 25 | import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser;
|
25 | 26 | import org.springframework.lang.Nullable;
|
26 | 27 | import org.springframework.util.Assert;
|
27 |
| -import java.util.regex.Pattern; |
28 |
| -import java.util.regex.Matcher; |
29 | 28 |
|
30 | 29 | /**
|
31 | 30 | * Similarity search request. Use the {@link SearchRequest#builder()} to create the
|
@@ -61,6 +60,8 @@ public class SearchRequest {
|
61 | 60 | @Nullable
|
62 | 61 | private Filter.Expression filterExpression;
|
63 | 62 |
|
| 63 | + private static final Map<Character, String> ESCAPE_TEXT = Map.of('\\', "\\\\", '.', "\\."); |
| 64 | + |
64 | 65 | /**
|
65 | 66 | * Copy an existing {@link SearchRequest.Builder} instance.
|
66 | 67 | * @param originalSearchRequest {@link SearchRequest} instance to copy.
|
@@ -193,7 +194,6 @@ public Builder similarityThresholdAll() {
|
193 | 194 | /**
|
194 | 195 | * Retrieves documents by query embedding similarity and matching the filters.
|
195 | 196 | * Value of 'null' means that no metadata filters will be applied to the search.
|
196 |
| - * |
197 | 197 | * For example if the {@link Document#getMetadata()} schema is:
|
198 | 198 | *
|
199 | 199 | * <pre>{@code
|
@@ -290,14 +290,24 @@ public Builder filterExpression(@Nullable String textExpression) {
|
290 | 290 | }
|
291 | 291 |
|
292 | 292 | 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 | + } |
299 | 309 | }
|
300 |
| - matcher.appendTail(sb); |
| 310 | + |
301 | 311 | return sb.toString();
|
302 | 312 | }
|
303 | 313 |
|
|
0 commit comments