001/*
002 *  Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.agentsflex.core.store;
017
018import com.agentsflex.core.store.condition.*;
019
020import java.util.*;
021import java.util.function.Consumer;
022
023public class SearchWrapper extends VectorData {
024
025    /**
026     * the default value of search data count
027     */
028    public static final int DEFAULT_MAX_RESULTS = 4;
029
030    /**
031     * search text, Vector store will convert the text to vector data
032     */
033    private String text;
034
035    /**
036     * search max result, like the sql "limit" in mysql
037     */
038    private Integer maxResults = DEFAULT_MAX_RESULTS;
039
040    /**
041     * The lowest correlation score, ranging from 0 to 1 (including 0 and 1). Only embeddings with a score of this value or higher will be returned.
042     * 0.0 indicates accepting any similarity or disabling similarity threshold filtering. A threshold of 1.0 indicates the need for a perfect match.
043     */
044    private Double minScore;
045
046    /**
047     * The flag of include vector data queries. If the current value is true and the vector content is null,
048     * the query text will be automatically converted into vector data through the vector store.
049     */
050    private boolean withVector = true;
051
052    /**
053     * query condition
054     */
055    private Condition condition;
056
057    /**
058     * query fields
059     */
060    private List<String> outputFields;
061
062    /**
063     * whether to output vector data
064     */
065    private boolean outputVector = false;
066
067
068    private List<String> partitionNames;
069
070
071    public String getText() {
072        return text;
073    }
074
075    public void setText(String text) {
076        this.text = text;
077    }
078
079    public SearchWrapper text(String text) {
080        setText(text);
081        return this;
082    }
083
084    public Integer getMaxResults() {
085        return maxResults;
086    }
087
088    public void setMaxResults(Integer maxResults) {
089        this.maxResults = maxResults;
090    }
091
092    public SearchWrapper maxResults(Integer maxResults) {
093        setMaxResults(maxResults);
094        return this;
095    }
096
097    public Double getMinScore() {
098        return minScore;
099    }
100
101    public void setMinScore(Double minScore) {
102        this.minScore = minScore;
103    }
104
105    public SearchWrapper minScore(Double minScore) {
106        setMinScore(minScore);
107        return this;
108    }
109
110    public boolean isWithVector() {
111        return withVector;
112    }
113
114    public void setWithVector(boolean withVector) {
115        this.withVector = withVector;
116    }
117
118    public SearchWrapper withVector(Boolean withVector) {
119        setWithVector(withVector);
120        return this;
121    }
122
123    public Condition getCondition() {
124        return condition;
125    }
126
127    public void setCondition(Condition condition) {
128        this.condition = condition;
129    }
130
131    public List<String> getOutputFields() {
132        return outputFields;
133    }
134
135    public void setOutputFields(List<String> outputFields) {
136        this.outputFields = outputFields;
137    }
138
139    public SearchWrapper outputFields(Collection<String> outputFields) {
140        setOutputFields(new ArrayList<>(outputFields));
141        return this;
142    }
143
144    public SearchWrapper outputFields(String... outputFields) {
145        setOutputFields(Arrays.asList(outputFields));
146        return this;
147    }
148
149    public boolean isOutputVector() {
150        return outputVector;
151    }
152
153    public void setOutputVector(boolean outputVector) {
154        this.outputVector = outputVector;
155    }
156
157    public SearchWrapper outputVector(boolean outputVector) {
158        setOutputVector(outputVector);
159        return this;
160    }
161
162
163    public SearchWrapper eq(String key, Object value) {
164        return eq(Connector.AND, key, value);
165    }
166
167    public SearchWrapper eq(Connector connector, String key, Object value) {
168        if (this.condition == null) {
169            this.condition = new Condition(ConditionType.EQ, new Key(key), new Value(value));
170        } else {
171            this.condition.connect(new Condition(ConditionType.EQ, new Key(key), new Value(value)), connector);
172        }
173        return this;
174    }
175
176    public SearchWrapper ne(String key, Object value) {
177        return ne(Connector.AND, key, value);
178    }
179
180    public SearchWrapper ne(Connector connector, String key, Object value) {
181        if (this.condition == null) {
182            this.condition = new Condition(ConditionType.NE, new Key(key), new Value(value));
183        } else {
184            this.condition.connect(new Condition(ConditionType.NE, new Key(key), new Value(value)), connector);
185        }
186        return this;
187    }
188
189    public SearchWrapper gt(String key, Object value) {
190        return gt(Connector.AND, key, value);
191    }
192
193    public SearchWrapper gt(Connector connector, String key, Object value) {
194        if (this.condition == null) {
195            this.condition = new Condition(ConditionType.GT, new Key(key), new Value(value));
196        } else {
197            this.condition.connect(new Condition(ConditionType.GT, new Key(key), new Value(value)), connector);
198        }
199        return this;
200    }
201
202
203    public SearchWrapper ge(String key, Object value) {
204        return ge(Connector.AND, key, value);
205    }
206
207    public SearchWrapper ge(Connector connector, String key, Object value) {
208        if (this.condition == null) {
209            this.condition = new Condition(ConditionType.GE, new Key(key), new Value(value));
210        } else {
211            this.condition.connect(new Condition(ConditionType.GE, new Key(key), new Value(value)), connector);
212        }
213        return this;
214    }
215
216
217    public SearchWrapper lt(String key, Object value) {
218        return lt(Connector.AND, key, value);
219    }
220
221    public SearchWrapper lt(Connector connector, String key, Object value) {
222        if (this.condition == null) {
223            this.condition = new Condition(ConditionType.LT, new Key(key), new Value(value));
224        } else {
225            this.condition.connect(new Condition(ConditionType.LT, new Key(key), new Value(value)), connector);
226        }
227        return this;
228    }
229
230
231    public SearchWrapper le(String key, Object value) {
232        return le(Connector.AND, key, value);
233    }
234
235    public SearchWrapper le(Connector connector, String key, Object value) {
236        if (this.condition == null) {
237            this.condition = new Condition(ConditionType.LE, new Key(key), new Value(value));
238        } else {
239            this.condition.connect(new Condition(ConditionType.LE, new Key(key), new Value(value)), connector);
240        }
241        return this;
242    }
243
244
245    public SearchWrapper in(String key, Collection<?> values) {
246        return in(Connector.AND, key, values);
247    }
248
249    public SearchWrapper in(Connector connector, String key, Collection<?> values) {
250        if (this.condition == null) {
251            this.condition = new Condition(ConditionType.IN, new Key(key), new Value(values.toArray()));
252        } else {
253            this.condition.connect(new Condition(ConditionType.IN, new Key(key), new Value(values.toArray())), connector);
254        }
255        return this;
256    }
257
258    public SearchWrapper min(String key, Object value) {
259        return min(Connector.AND, key, value);
260    }
261
262    public SearchWrapper min(Connector connector, String key, Object value) {
263        if (this.condition == null) {
264            this.condition = new Condition(ConditionType.NIN, new Key(key), new Value(value));
265        } else {
266            this.condition.connect(new Condition(ConditionType.NIN, new Key(key), new Value(value)), connector);
267        }
268        return this;
269    }
270
271    public SearchWrapper between(String key, Object start, Object end) {
272        return between(Connector.AND, key, start, end);
273    }
274
275    public SearchWrapper between(Connector connector, String key, Object start, Object end) {
276        if (this.condition == null) {
277            this.condition = new Condition(ConditionType.BETWEEN, new Key(key), new Value(start, end));
278        } else {
279            this.condition.connect(new Condition(ConditionType.BETWEEN, new Key(key), new Value(start, end)), connector);
280        }
281        return this;
282    }
283
284
285    public SearchWrapper group(SearchWrapper wrapper) {
286        return group(wrapper.condition);
287    }
288
289    public SearchWrapper group(Condition condition) {
290        if (this.condition == null) {
291            this.condition = new Group(condition);
292        } else {
293            this.condition.connect(new Group(condition), Connector.AND);
294        }
295        return this;
296    }
297
298    public SearchWrapper group(Consumer<SearchWrapper> consumer) {
299        SearchWrapper newWrapper = new SearchWrapper();
300        consumer.accept(newWrapper);
301        Condition condition = newWrapper.condition;
302        if (condition != null) {
303            group(condition);
304        }
305        return this;
306    }
307
308    public SearchWrapper andCriteria(Consumer<SearchWrapper> consumer) {
309        return group(consumer);
310    }
311
312    public SearchWrapper orCriteria(Consumer<SearchWrapper> consumer) {
313        SearchWrapper newWrapper = new SearchWrapper();
314        consumer.accept(newWrapper);
315        Condition condition = newWrapper.condition;
316        if (condition != null) {
317            if (this.condition == null) {
318                this.condition = new Group(condition);
319            } else {
320                this.condition.connect(new Group(condition), Connector.OR);
321            }
322        }
323        return this;
324    }
325
326    /**
327     * Convert to expressions for filtering conditions, with different expression requirements for each vendor.
328     * Customized adaptor can be achieved through ExpressionAdaptor
329     */
330    public String toFilterExpression() {
331        return toFilterExpression(ExpressionAdaptor.DEFAULT);
332    }
333
334    public String toFilterExpression(ExpressionAdaptor adaptor) {
335        if (this.condition == null) {
336            return null;
337        } else {
338            Objects.requireNonNull(adaptor, "adaptor must not be null");
339            return this.condition.toExpression(adaptor);
340        }
341    }
342
343    public List<String> getPartitionNames() {
344        return partitionNames;
345    }
346
347    public void setPartitionNames(List<String> partitionNames) {
348        this.partitionNames = partitionNames;
349    }
350}