1package cbft
2
3import (
4	"encoding/json"
5)
6
7// FtsQuery represents an FTS query for a search query.
8type FtsQuery interface {
9}
10
11type ftsQueryBase struct {
12	options map[string]interface{}
13}
14
15func newFtsQueryBase() ftsQueryBase {
16	return ftsQueryBase{
17		options: make(map[string]interface{}),
18	}
19}
20
21// MarshalJSON marshal's this query to JSON for the FTS REST API.
22func (q ftsQueryBase) MarshalJSON() ([]byte, error) {
23	return json.Marshal(q.options)
24}
25
26// MatchQuery represents a FTS match query.
27type MatchQuery struct {
28	ftsQueryBase
29}
30
31// NewMatchQuery creates a new MatchQuery.
32func NewMatchQuery(match string) *MatchQuery {
33	q := &MatchQuery{newFtsQueryBase()}
34	q.options["match"] = match
35	return q
36}
37
38// Field specifies the field for this query.
39func (q *MatchQuery) Field(field string) *MatchQuery {
40	q.options["field"] = field
41	return q
42}
43
44// Analyzer specifies the analyzer to use for this query.
45func (q *MatchQuery) Analyzer(analyzer string) *MatchQuery {
46	q.options["analyzer"] = analyzer
47	return q
48}
49
50// PrefixLength specifies the prefix length from this query.
51func (q *MatchQuery) PrefixLength(length int) *MatchQuery {
52	q.options["prefix_length"] = length
53	return q
54}
55
56// Fuzziness specifies the fuziness for this query.
57func (q *MatchQuery) Fuzziness(fuzziness int) *MatchQuery {
58	q.options["fuzziness"] = fuzziness
59	return q
60}
61
62// Boost specifies the boost for this query.
63func (q *MatchQuery) Boost(boost float32) *MatchQuery {
64	q.options["boost"] = boost
65	return q
66}
67
68// MatchPhraseQuery represents a FTS match phrase query.
69type MatchPhraseQuery struct {
70	ftsQueryBase
71}
72
73// NewMatchPhraseQuery creates a new MatchPhraseQuery
74func NewMatchPhraseQuery(phrase string) *MatchPhraseQuery {
75	q := &MatchPhraseQuery{newFtsQueryBase()}
76	q.options["match_phrase"] = phrase
77	return q
78}
79
80// Field specifies the field for this query.
81func (q *MatchPhraseQuery) Field(field string) *MatchPhraseQuery {
82	q.options["field"] = field
83	return q
84}
85
86// Analyzer specifies the analyzer to use for this query.
87func (q *MatchPhraseQuery) Analyzer(analyzer string) *MatchPhraseQuery {
88	q.options["analyzer"] = analyzer
89	return q
90}
91
92// Boost specifies the boost for this query.
93func (q *MatchPhraseQuery) Boost(boost float32) *MatchPhraseQuery {
94	q.options["boost"] = boost
95	return q
96}
97
98// RegexpQuery represents a FTS regular expression query.
99type RegexpQuery struct {
100	ftsQueryBase
101}
102
103// NewRegexpQuery creates a new RegexpQuery.
104func NewRegexpQuery(regexp string) *RegexpQuery {
105	q := &RegexpQuery{newFtsQueryBase()}
106	q.options["regexp"] = regexp
107	return q
108}
109
110// Field specifies the field for this query.
111func (q *RegexpQuery) Field(field string) *RegexpQuery {
112	q.options["field"] = field
113	return q
114}
115
116// Boost specifies the boost for this query.
117func (q *RegexpQuery) Boost(boost float32) *RegexpQuery {
118	q.options["boost"] = boost
119	return q
120}
121
122// QueryStringQuery represents a FTS string query.
123type QueryStringQuery struct {
124	ftsQueryBase
125}
126
127// NewQueryStringQuery creates a new StringQuery.
128func NewQueryStringQuery(query string) *QueryStringQuery {
129	q := &QueryStringQuery{newFtsQueryBase()}
130	q.options["query"] = query
131	return q
132}
133
134// Boost specifies the boost for this query.
135func (q *QueryStringQuery) Boost(boost float32) *QueryStringQuery {
136	q.options["boost"] = boost
137	return q
138}
139
140// NumericRangeQuery represents a FTS numeric range query.
141type NumericRangeQuery struct {
142	ftsQueryBase
143}
144
145// NewNumericRangeQuery creates a new NumericRangeQuery.
146func NewNumericRangeQuery() *NumericRangeQuery {
147	q := &NumericRangeQuery{newFtsQueryBase()}
148	return q
149}
150
151// Min specifies the minimum value and inclusiveness for this range query.
152func (q *NumericRangeQuery) Min(min float32, inclusive bool) *NumericRangeQuery {
153	q.options["min"] = min
154	q.options["inclusive_min"] = inclusive
155	return q
156}
157
158// Max specifies the maximum value and inclusiveness for this range query.
159func (q *NumericRangeQuery) Max(max float32, inclusive bool) *NumericRangeQuery {
160	q.options["max"] = max
161	q.options["inclusive_max"] = inclusive
162	return q
163}
164
165// Field specifies the field for this query.
166func (q *NumericRangeQuery) Field(field string) *NumericRangeQuery {
167	q.options["field"] = field
168	return q
169}
170
171// Boost specifies the boost for this query.
172func (q *NumericRangeQuery) Boost(boost float32) *NumericRangeQuery {
173	q.options["boost"] = boost
174	return q
175}
176
177// DateRangeQuery represents a FTS date range query.
178type DateRangeQuery struct {
179	ftsQueryBase
180}
181
182// NewDateRangeQuery creates a new DateRangeQuery.
183func NewDateRangeQuery() *DateRangeQuery {
184	q := &DateRangeQuery{newFtsQueryBase()}
185	return q
186}
187
188// Start specifies the start value and inclusiveness for this range query.
189func (q *DateRangeQuery) Start(start string, inclusive bool) *DateRangeQuery {
190	q.options["start"] = start
191	q.options["inclusive_start"] = inclusive
192	return q
193}
194
195// End specifies the end value and inclusiveness for this range query.
196func (q *DateRangeQuery) End(end string, inclusive bool) *DateRangeQuery {
197	q.options["end"] = end
198	q.options["inclusive_end"] = inclusive
199	return q
200}
201
202// DateTimeParser specifies which date time string parser to use.
203func (q *DateRangeQuery) DateTimeParser(parser string) *DateRangeQuery {
204	q.options["datetime_parser"] = parser
205	return q
206}
207
208// Field specifies the field for this query.
209func (q *DateRangeQuery) Field(field string) *DateRangeQuery {
210	q.options["field"] = field
211	return q
212}
213
214// Boost specifies the boost for this query.
215func (q *DateRangeQuery) Boost(boost float32) *DateRangeQuery {
216	q.options["boost"] = boost
217	return q
218}
219
220// ConjunctionQuery represents a FTS conjunction query.
221type ConjunctionQuery struct {
222	ftsQueryBase
223}
224
225// NewConjunctionQuery creates a new ConjunctionQuery.
226func NewConjunctionQuery(queries ...FtsQuery) *ConjunctionQuery {
227	q := &ConjunctionQuery{newFtsQueryBase()}
228	q.options["conjuncts"] = []FtsQuery{}
229	return q.And(queries...)
230}
231
232// And adds new predicate queries to this conjunction query.
233func (q *ConjunctionQuery) And(queries ...FtsQuery) *ConjunctionQuery {
234	q.options["conjuncts"] = append(q.options["conjuncts"].([]FtsQuery), queries...)
235	return q
236}
237
238// Boost specifies the boost for this query.
239func (q *ConjunctionQuery) Boost(boost float32) *ConjunctionQuery {
240	q.options["boost"] = boost
241	return q
242}
243
244// DisjunctionQuery represents a FTS disjunction query.
245type DisjunctionQuery struct {
246	ftsQueryBase
247}
248
249// NewDisjunctionQuery creates a new DisjunctionQuery.
250func NewDisjunctionQuery(queries ...FtsQuery) *DisjunctionQuery {
251	q := &DisjunctionQuery{newFtsQueryBase()}
252	q.options["disjuncts"] = []FtsQuery{}
253	return q.Or(queries...)
254}
255
256// Or adds new predicate queries to this disjunction query.
257func (q *DisjunctionQuery) Or(queries ...FtsQuery) *DisjunctionQuery {
258	q.options["disjuncts"] = append(q.options["disjuncts"].([]FtsQuery), queries...)
259	return q
260}
261
262// Boost specifies the boost for this query.
263func (q *DisjunctionQuery) Boost(boost float32) *DisjunctionQuery {
264	q.options["boost"] = boost
265	return q
266}
267
268type booleanQueryData struct {
269	Must    *ConjunctionQuery `json:"must,omitempty"`
270	Should  *DisjunctionQuery `json:"should,omitempty"`
271	MustNot *DisjunctionQuery `json:"must_not,omitempty"`
272	Boost   float32           `json:"boost,omitempty"`
273}
274
275// BooleanQuery represents a FTS boolean query.
276type BooleanQuery struct {
277	data      booleanQueryData
278	shouldMin int
279}
280
281// NewBooleanQuery creates a new BooleanQuery.
282func NewBooleanQuery() *BooleanQuery {
283	q := &BooleanQuery{}
284	return q
285}
286
287// Must specifies a query which must match.
288func (q *BooleanQuery) Must(query FtsQuery) *BooleanQuery {
289	switch val := query.(type) {
290	case ConjunctionQuery:
291		q.data.Must = &val
292	case *ConjunctionQuery:
293		q.data.Must = val
294	default:
295		q.data.Must = NewConjunctionQuery(val)
296	}
297	return q
298}
299
300// Should specifies a query which should match.
301func (q *BooleanQuery) Should(query FtsQuery) *BooleanQuery {
302	switch val := query.(type) {
303	case DisjunctionQuery:
304		q.data.Should = &val
305	case *DisjunctionQuery:
306		q.data.Should = val
307	default:
308		q.data.Should = NewDisjunctionQuery(val)
309	}
310	return q
311}
312
313// MustNot specifies a query which must not match.
314func (q *BooleanQuery) MustNot(query FtsQuery) *BooleanQuery {
315	switch val := query.(type) {
316	case DisjunctionQuery:
317		q.data.MustNot = &val
318	case *DisjunctionQuery:
319		q.data.MustNot = val
320	default:
321		q.data.MustNot = NewDisjunctionQuery(val)
322	}
323	return q
324}
325
326// ShouldMin specifies the minimum value before the should query will boost.
327func (q *BooleanQuery) ShouldMin(min int) *BooleanQuery {
328	q.shouldMin = min
329	return q
330}
331
332// Boost specifies the boost for this query.
333func (q *BooleanQuery) Boost(boost float32) *BooleanQuery {
334	q.data.Boost = boost
335	return q
336}
337
338// MarshalJSON marshal's this query to JSON for the FTS REST API.
339func (q *BooleanQuery) MarshalJSON() ([]byte, error) {
340	if q.data.Should != nil {
341		q.data.Should.options["min"] = q.shouldMin
342	}
343	bytes, err := json.Marshal(q.data)
344	if q.data.Should != nil {
345		delete(q.data.Should.options, "min")
346	}
347	return bytes, err
348}
349
350// WildcardQuery represents a FTS wildcard query.
351type WildcardQuery struct {
352	ftsQueryBase
353}
354
355// NewWildcardQuery creates a new WildcardQuery.
356func NewWildcardQuery(wildcard string) *WildcardQuery {
357	q := &WildcardQuery{newFtsQueryBase()}
358	q.options["wildcard"] = wildcard
359	return q
360}
361
362// Field specifies the field for this query.
363func (q *WildcardQuery) Field(field string) *WildcardQuery {
364	q.options["field"] = field
365	return q
366}
367
368// Boost specifies the boost for this query.
369func (q *WildcardQuery) Boost(boost float32) *WildcardQuery {
370	q.options["boost"] = boost
371	return q
372}
373
374// DocIdQuery represents a FTS document id query.
375type DocIdQuery struct {
376	ftsQueryBase
377}
378
379// NewDocIdQuery creates a new DocIdQuery.
380func NewDocIdQuery(ids ...string) *DocIdQuery {
381	q := &DocIdQuery{newFtsQueryBase()}
382	q.options["ids"] = []string{}
383	return q.AddDocIds(ids...)
384}
385
386// AddDocIds adds addition document ids to this query.
387func (q *DocIdQuery) AddDocIds(ids ...string) *DocIdQuery {
388	q.options["ids"] = append(q.options["ids"].([]string), ids...)
389	return q
390}
391
392// Field specifies the field for this query.
393func (q *DocIdQuery) Field(field string) *DocIdQuery {
394	q.options["field"] = field
395	return q
396}
397
398// Boost specifies the boost for this query.
399func (q *DocIdQuery) Boost(boost float32) *DocIdQuery {
400	q.options["boost"] = boost
401	return q
402}
403
404// BooleanFieldQuery represents a FTS boolean field query.
405type BooleanFieldQuery struct {
406	ftsQueryBase
407}
408
409// NewBooleanFieldQuery creates a new BooleanFieldQuery.
410func NewBooleanFieldQuery(val bool) *BooleanFieldQuery {
411	q := &BooleanFieldQuery{newFtsQueryBase()}
412	q.options["bool"] = val
413	return q
414}
415
416// Field specifies the field for this query.
417func (q *BooleanFieldQuery) Field(field string) *BooleanFieldQuery {
418	q.options["field"] = field
419	return q
420}
421
422// Boost specifies the boost for this query.
423func (q *BooleanFieldQuery) Boost(boost float32) *BooleanFieldQuery {
424	q.options["boost"] = boost
425	return q
426}
427
428// TermQuery represents a FTS term query.
429type TermQuery struct {
430	ftsQueryBase
431}
432
433// NewTermQuery creates a new TermQuery.
434func NewTermQuery(term string) *TermQuery {
435	q := &TermQuery{newFtsQueryBase()}
436	q.options["term"] = term
437	return q
438}
439
440// Field specifies the field for this query.
441func (q *TermQuery) Field(field string) *TermQuery {
442	q.options["field"] = field
443	return q
444}
445
446// PrefixLength specifies the prefix length from this query.
447func (q *TermQuery) PrefixLength(length int) *TermQuery {
448	q.options["prefix_length"] = length
449	return q
450}
451
452// Fuzziness specifies the fuziness for this query.
453func (q *TermQuery) Fuzziness(fuzziness int) *TermQuery {
454	q.options["fuzziness"] = fuzziness
455	return q
456}
457
458// Boost specifies the boost for this query.
459func (q *TermQuery) Boost(boost float32) *TermQuery {
460	q.options["boost"] = boost
461	return q
462}
463
464// PhraseQuery represents a FTS phrase query.
465type PhraseQuery struct {
466	ftsQueryBase
467}
468
469// NewPhraseQuery creates a new PhraseQuery.
470func NewPhraseQuery(terms ...string) *PhraseQuery {
471	q := &PhraseQuery{newFtsQueryBase()}
472	q.options["terms"] = terms
473	return q
474}
475
476// Field specifies the field for this query.
477func (q *PhraseQuery) Field(field string) *PhraseQuery {
478	q.options["field"] = field
479	return q
480}
481
482// Boost specifies the boost for this query.
483func (q *PhraseQuery) Boost(boost float32) *PhraseQuery {
484	q.options["boost"] = boost
485	return q
486}
487
488// PrefixQuery represents a FTS prefix query.
489type PrefixQuery struct {
490	ftsQueryBase
491}
492
493// NewPrefixQuery creates a new PrefixQuery.
494func NewPrefixQuery(prefix string) *PrefixQuery {
495	q := &PrefixQuery{newFtsQueryBase()}
496	q.options["prefix"] = prefix
497	return q
498}
499
500// Field specifies the field for this query.
501func (q *PrefixQuery) Field(field string) *PrefixQuery {
502	q.options["field"] = field
503	return q
504}
505
506// Boost specifies the boost for this query.
507func (q *PrefixQuery) Boost(boost float32) *PrefixQuery {
508	q.options["boost"] = boost
509	return q
510}
511
512// MatchAllQuery represents a FTS match all query.
513type MatchAllQuery struct {
514}
515
516// NewMatchAllQuery creates a new MatchAllQuery.
517func NewMatchAllQuery(prefix string) *MatchAllQuery {
518	return &MatchAllQuery{}
519}
520
521// MatchNoneQuery represents a FTS match none query.
522type MatchNoneQuery struct {
523}
524
525// NewMatchNoneQuery creates a new MatchNoneQuery.
526func NewMatchNoneQuery(prefix string) *MatchNoneQuery {
527	return &MatchNoneQuery{}
528}
529
530// TermRangeQuery represents a FTS term range query.
531type TermRangeQuery struct {
532	ftsQueryBase
533}
534
535// NewTermRangeQuery creates a new TermRangeQuery.
536func NewTermRangeQuery(term string) *TermRangeQuery {
537	q := &TermRangeQuery{newFtsQueryBase()}
538	q.options["term"] = term
539	return q
540}
541
542// Field specifies the field for this query.
543func (q *TermRangeQuery) Field(field string) *TermRangeQuery {
544	q.options["field"] = field
545	return q
546}
547
548// Min specifies the minimum value and inclusiveness for this range query.
549func (q *TermRangeQuery) Min(min string, inclusive bool) *TermRangeQuery {
550	q.options["min"] = min
551	q.options["inclusive_min"] = inclusive
552	return q
553}
554
555// Max specifies the maximum value and inclusiveness for this range query.
556func (q *TermRangeQuery) Max(max string, inclusive bool) *TermRangeQuery {
557	q.options["max"] = max
558	q.options["inclusive_max"] = inclusive
559	return q
560}
561
562// Boost specifies the boost for this query.
563func (q *TermRangeQuery) Boost(boost float32) *TermRangeQuery {
564	q.options["boost"] = boost
565	return q
566}
567
568// GeoDistanceQuery represents a FTS geographical distance query.
569type GeoDistanceQuery struct {
570	ftsQueryBase
571}
572
573// NewGeoDistanceQuery creates a new GeoDistanceQuery.
574func NewGeoDistanceQuery(lat, lon float64, distance string) *GeoDistanceQuery {
575	q := &GeoDistanceQuery{newFtsQueryBase()}
576	q.options["location"] = []float64{lon, lat}
577	q.options["distance"] = distance
578	return q
579}
580
581// Field specifies the field for this query.
582func (q *GeoDistanceQuery) Field(field string) *GeoDistanceQuery {
583	q.options["field"] = field
584	return q
585}
586
587// Boost specifies the boost for this query.
588func (q *GeoDistanceQuery) Boost(boost float32) *GeoDistanceQuery {
589	q.options["boost"] = boost
590	return q
591}
592
593// GeoBoundingBoxQuery represents a FTS geographical bounding box query.
594type GeoBoundingBoxQuery struct {
595	ftsQueryBase
596}
597
598// NewGeoBoundingBoxQuery creates a new GeoBoundingBoxQuery.
599func NewGeoBoundingBoxQuery(tlLat, tlLon, brLat, brLon float64) *GeoBoundingBoxQuery {
600	q := &GeoBoundingBoxQuery{newFtsQueryBase()}
601	q.options["top_left"] = []float64{tlLon, tlLat}
602	q.options["bottom_right"] = []float64{brLon, brLat}
603	return q
604}
605
606// Field specifies the field for this query.
607func (q *GeoBoundingBoxQuery) Field(field string) *GeoBoundingBoxQuery {
608	q.options["field"] = field
609	return q
610}
611
612// Boost specifies the boost for this query.
613func (q *GeoBoundingBoxQuery) Boost(boost float32) *GeoBoundingBoxQuery {
614	q.options["boost"] = boost
615	return q
616}
617