Skip to content

Commit bccf96e

Browse files
committed
Add Intervals Query
This commit adds the Intervals Query. See https://www.elastic.co/guide/en/elasticsearch/reference/7.5/query-dsl-intervals-query.html for details. Close #1244
1 parent cae992f commit bccf96e

10 files changed

+402
-173
lines changed

search_queries_interval.go

+18-9
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@
44

55
package elastic
66

7-
// IntervalQueryRule represents the generic matching interval rule interface. Interval Rule is actually
8-
// just a Query, but may be used only inside IntervalQuery. An extra method is added
9-
// just to separate all its implementations (*Rule objects) from other query objects
7+
// IntervalQueryRule represents the generic matching interval rule interface.
8+
// Interval Rule is actually just a Query, but may be used only inside
9+
// IntervalQuery. An extra method is added just to shield its
10+
// implementations (*Rule objects) from other query objects.
11+
//
12+
// See https://www.elastic.co/guide/en/elasticsearch/reference/7.5/query-dsl-intervals-query.html
13+
// for details.
1014
type IntervalQueryRule interface {
1115
Query
1216

13-
// isIntervalQueryRule is never actually called, and is used just for Rule to differ from standard Query
17+
// isIntervalQueryRule is never actually called, and is used just for Rule to
18+
// differ from standard Query.
1419
isIntervalQueryRule() bool
1520
}
1621

17-
// IntervalQuery returns documents based on the order and proximity of matching terms
22+
// IntervalQuery returns documents based on the order and proximity of matching terms.
1823
//
1924
// For more details, see
20-
// https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl-intervals-query.html
25+
// https://www.elastic.co/guide/en/elasticsearch/reference/7.5/query-dsl-intervals-query.html
2126
type IntervalQuery struct {
2227
field string
2328
rule IntervalQueryRule
@@ -30,14 +35,18 @@ func NewIntervalQuery(field string, rule IntervalQueryRule) *IntervalQuery {
3035

3136
// Source returns JSON for the function score query.
3237
func (q *IntervalQuery) Source() (interface{}, error) {
38+
// {
39+
// "intervals" : { ... }
40+
// }
3341
source := make(map[string]interface{})
42+
params := make(map[string]interface{})
43+
source["intervals"] = params
3444

35-
ruleSrc, err := q.rule.Source()
45+
src, err := q.rule.Source()
3646
if err != nil {
3747
return nil, err
3848
}
39-
40-
source[q.field] = ruleSrc
49+
params[q.field] = src
4150

4251
return source, nil
4352
}

search_queries_interval_filter.go

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package elastic
2+
3+
var (
4+
_ IntervalQueryRule = (*IntervalQueryFilter)(nil)
5+
)
6+
7+
// IntervalQueryFilter specifies filters used in some
8+
// IntervalQueryRule implementations, e.g. IntervalQueryRuleAllOf.
9+
//
10+
// See https://www.elastic.co/guide/en/elasticsearch/reference/7.5/query-dsl-intervals-query.html#interval_filter
11+
// for details.
12+
type IntervalQueryFilter struct {
13+
after IntervalQueryRule
14+
before IntervalQueryRule
15+
containedBy IntervalQueryRule
16+
containing IntervalQueryRule
17+
overlapping IntervalQueryRule
18+
notContainedBy IntervalQueryRule
19+
notContaining IntervalQueryRule
20+
notOverlapping IntervalQueryRule
21+
script *Script
22+
}
23+
24+
// NewIntervalQueryFilter initializes and creates a new
25+
// IntervalQueryFilter.
26+
func NewIntervalQueryFilter() *IntervalQueryFilter {
27+
return &IntervalQueryFilter{}
28+
}
29+
30+
// After specifies the query to be used to return intervals that follow
31+
// an interval from the filter rule.
32+
func (r *IntervalQueryFilter) After(after IntervalQueryRule) *IntervalQueryFilter {
33+
r.after = after
34+
return r
35+
}
36+
37+
// Before specifies the query to be used to return intervals that occur
38+
// before an interval from the filter rule.
39+
func (r *IntervalQueryFilter) Before(before IntervalQueryRule) *IntervalQueryFilter {
40+
r.before = before
41+
return r
42+
}
43+
44+
// ContainedBy specifies the query to be used to return intervals contained
45+
// by an interval from the filter rule.
46+
func (r *IntervalQueryFilter) ContainedBy(containedBy IntervalQueryRule) *IntervalQueryFilter {
47+
r.containedBy = containedBy
48+
return r
49+
}
50+
51+
// Containing specifies the query to be used to return intervals that contain an
52+
// interval from the filter rule.
53+
func (r *IntervalQueryFilter) Containing(containing IntervalQueryRule) *IntervalQueryFilter {
54+
r.containing = containing
55+
return r
56+
}
57+
58+
// Overlapping specifies the query to be used to return intervals that overlap
59+
// with an interval from the filter rule.
60+
func (r *IntervalQueryFilter) Overlapping(overlapping IntervalQueryRule) *IntervalQueryFilter {
61+
r.overlapping = overlapping
62+
return r
63+
}
64+
65+
// NotContainedBy specifies the query to be used to return intervals that are NOT
66+
// contained by an interval from the filter rule.
67+
func (r *IntervalQueryFilter) NotContainedBy(notContainedBy IntervalQueryRule) *IntervalQueryFilter {
68+
r.notContainedBy = notContainedBy
69+
return r
70+
}
71+
72+
// NotContaining specifies the query to be used to return intervals that do NOT
73+
// contain an interval from the filter rule.
74+
func (r *IntervalQueryFilter) NotContaining(notContaining IntervalQueryRule) *IntervalQueryFilter {
75+
r.notContaining = notContaining
76+
return r
77+
}
78+
79+
// NotOverlapping specifies the query to be used to return intervals that do NOT
80+
// overlap with an interval from the filter rule.
81+
func (r *IntervalQueryFilter) NotOverlapping(notOverlapping IntervalQueryRule) *IntervalQueryFilter {
82+
r.notOverlapping = notOverlapping
83+
return r
84+
}
85+
86+
// Script allows a script to be used to return matching documents. The script
87+
// must return a boolean value, true or false.
88+
func (r *IntervalQueryFilter) Script(script *Script) *IntervalQueryFilter {
89+
r.script = script
90+
return r
91+
}
92+
93+
// Source returns JSON for the function score query.
94+
func (r *IntervalQueryFilter) Source() (interface{}, error) {
95+
source := make(map[string]interface{})
96+
97+
if r.before != nil {
98+
src, err := r.before.Source()
99+
if err != nil {
100+
return nil, err
101+
}
102+
source["before"] = src
103+
}
104+
105+
if r.after != nil {
106+
src, err := r.after.Source()
107+
if err != nil {
108+
return nil, err
109+
}
110+
source["after"] = src
111+
}
112+
113+
if r.containedBy != nil {
114+
src, err := r.containedBy.Source()
115+
if err != nil {
116+
return nil, err
117+
}
118+
source["contained_by"] = src
119+
}
120+
121+
if r.containing != nil {
122+
src, err := r.containing.Source()
123+
if err != nil {
124+
return nil, err
125+
}
126+
source["containing"] = src
127+
}
128+
129+
if r.overlapping != nil {
130+
src, err := r.overlapping.Source()
131+
if err != nil {
132+
return nil, err
133+
}
134+
source["overlapping"] = src
135+
}
136+
137+
if r.notContainedBy != nil {
138+
src, err := r.notContainedBy.Source()
139+
if err != nil {
140+
return nil, err
141+
}
142+
source["not_contained_by"] = src
143+
}
144+
145+
if r.notContaining != nil {
146+
src, err := r.notContaining.Source()
147+
if err != nil {
148+
return nil, err
149+
}
150+
source["not_containing"] = src
151+
}
152+
153+
if r.notOverlapping != nil {
154+
src, err := r.notOverlapping.Source()
155+
if err != nil {
156+
return nil, err
157+
}
158+
source["not_overlapping"] = src
159+
}
160+
161+
if r.script != nil {
162+
src, err := r.script.Source()
163+
if err != nil {
164+
return nil, err
165+
}
166+
source["script"] = src
167+
}
168+
169+
return source, nil
170+
}
171+
172+
// isIntervalQueryRule implements the marker interface.
173+
func (r *IntervalQueryFilter) isIntervalQueryRule() bool {
174+
return true
175+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2012-present Oliver Eilhard. All rights reserved.
2+
// Use of this source code is governed by a MIT-license.
3+
// See http://olivere.mit-license.org/license.txt for details.
4+
5+
package elastic
6+
7+
import (
8+
"context"
9+
"encoding/json"
10+
"testing"
11+
)
12+
13+
func TestIntervalQuery_Integration(t *testing.T) {
14+
// client := setupTestClientAndCreateIndexAndAddDocs(t, SetTraceLog(log.New(os.Stdout, "", log.LstdFlags)))
15+
client := setupTestClientAndCreateIndexAndAddDocs(t)
16+
17+
q := NewIntervalQuery(
18+
"message",
19+
NewIntervalQueryRuleAllOf(
20+
NewIntervalQueryRuleAnyOf(
21+
NewIntervalQueryRuleMatch("Golang").Ordered(true),
22+
NewIntervalQueryRuleMatch("Cycling").MaxGaps(0).Filter(
23+
NewIntervalQueryFilter().NotContaining(
24+
NewIntervalQueryRuleMatch("Hockey"),
25+
),
26+
),
27+
),
28+
).Ordered(true),
29+
)
30+
31+
// Match all should return all documents
32+
searchResult, err := client.Search().
33+
Index(testIndexName).
34+
Query(q).
35+
Size(10).
36+
Pretty(true).
37+
Do(context.TODO())
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
if searchResult.Hits == nil {
42+
t.Errorf("expected SearchResult.Hits != nil; got nil")
43+
}
44+
if got, want := searchResult.TotalHits(), int64(2); got != want {
45+
t.Errorf("expected SearchResult.TotalHits() = %d; got %d", want, got)
46+
}
47+
if got, want := len(searchResult.Hits.Hits), 2; got != want {
48+
t.Errorf("expected len(SearchResult.Hits.Hits) = %d; got %d", want, got)
49+
}
50+
51+
for _, hit := range searchResult.Hits.Hits {
52+
if hit.Index != testIndexName {
53+
t.Errorf("expected SearchResult.Hits.Hit.Index = %q; got %q", testIndexName, hit.Index)
54+
}
55+
item := make(map[string]interface{})
56+
err := json.Unmarshal(hit.Source, &item)
57+
if err != nil {
58+
t.Fatal(err)
59+
}
60+
}
61+
}

search_queries_interval_rules_all_of.go

+21-5
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,42 @@
11
package elastic
22

3+
var (
4+
_ IntervalQueryRule = (*IntervalQueryRuleAllOf)(nil)
5+
)
6+
7+
// IntervalQueryRuleAllOf is an implementation of IntervalQueryRule.
8+
//
9+
// See https://www.elastic.co/guide/en/elasticsearch/reference/7.5/query-dsl-intervals-query.html#intervals-all_of
10+
// for details.
311
type IntervalQueryRuleAllOf struct {
412
intervals []IntervalQueryRule
513
maxGaps *int
614
ordered *bool
7-
filter *IntervalQueryRuleFilter
15+
filter *IntervalQueryFilter
816
}
917

10-
var _ IntervalQueryRule = &IntervalQueryRuleAllOf{}
11-
18+
// NewIntervalQueryRuleAllOf initializes and returns a new instance
19+
// of IntervalQueryRuleAllOf.
1220
func NewIntervalQueryRuleAllOf(intervals ...IntervalQueryRule) *IntervalQueryRuleAllOf {
1321
return &IntervalQueryRuleAllOf{intervals: intervals}
1422
}
1523

24+
// MaxGaps specifies the maximum number of positions between the matching
25+
// terms. Terms further apart than this are considered matches. Defaults to -1.
1626
func (r *IntervalQueryRuleAllOf) MaxGaps(maxGaps int) *IntervalQueryRuleAllOf {
1727
r.maxGaps = &maxGaps
1828
return r
1929
}
2030

31+
// Ordered, if true, indicates that matching terms must appear in their specified
32+
// order. Defaults to false.
2133
func (r *IntervalQueryRuleAllOf) Ordered(ordered bool) *IntervalQueryRuleAllOf {
2234
r.ordered = &ordered
2335
return r
2436
}
2537

26-
func (r *IntervalQueryRuleAllOf) Filter(filter *IntervalQueryRuleFilter) *IntervalQueryRuleAllOf {
38+
// Filter adds an additional interval filter.
39+
func (r *IntervalQueryRuleAllOf) Filter(filter *IntervalQueryFilter) *IntervalQueryRuleAllOf {
2740
r.filter = filter
2841
return r
2942
}
@@ -58,9 +71,12 @@ func (r *IntervalQueryRuleAllOf) Source() (interface{}, error) {
5871
source["filter"] = src
5972
}
6073

61-
return map[string]interface{}{"all_of": source}, nil
74+
return map[string]interface{}{
75+
"all_of": source,
76+
}, nil
6277
}
6378

79+
// isIntervalQueryRule implements the marker interface.
6480
func (r *IntervalQueryRuleAllOf) isIntervalQueryRule() bool {
6581
return true
6682
}

0 commit comments

Comments
 (0)