enhancement(search): implement kql to os dsl range-query
This commit is contained in:
@@ -18,7 +18,7 @@ func TestSearchHitToSearchMessageMatch(t *testing.T) {
|
||||
|
||||
hit := opensearchgoAPI.SearchHit{
|
||||
Score: 1.1,
|
||||
Source: json.RawMessage(opensearchtest.ToJSON(t, resource)),
|
||||
Source: json.RawMessage(opensearchtest.JSONMustMarshal(t, resource)),
|
||||
}
|
||||
match, err := opensearch.SearchHitToSearchMessageMatch(hit)
|
||||
assert.NoError(t, err)
|
||||
@@ -32,6 +32,6 @@ func TestSearchHitToSearchMessageMatch(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, resource.Audio.Bitrate, match.Entity.Audio.Bitrate)
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, audio), opensearchtest.ToJSON(t, match.Entity.Audio))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, audio), opensearchtest.JSONMustMarshal(t, match.Entity.Audio))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestEngine_Search(t *testing.T) {
|
||||
defer tc.Require.IndicesDelete([]string{index})
|
||||
|
||||
document := opensearchtest.Testdata.Resources.Full
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.ToJSON(t, document))
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.JSONMustMarshal(t, document))
|
||||
tc.Require.IndicesCount([]string{index}, "", 1)
|
||||
|
||||
engine, err := opensearch.NewEngine(index, tc.Client())
|
||||
@@ -58,7 +58,7 @@ func TestEngine_Search(t *testing.T) {
|
||||
deletedDocument.ID = "1$2!4"
|
||||
deletedDocument.Deleted = true
|
||||
|
||||
tc.Require.DocumentCreate(index, deletedDocument.ID, opensearchtest.ToJSON(t, deletedDocument))
|
||||
tc.Require.DocumentCreate(index, deletedDocument.ID, opensearchtest.JSONMustMarshal(t, deletedDocument))
|
||||
tc.Require.IndicesCount([]string{index}, "", 2)
|
||||
|
||||
resp, err := engine.Search(t.Context(), &searchService.SearchIndexRequest{
|
||||
@@ -105,7 +105,7 @@ func TestEngine_Delete(t *testing.T) {
|
||||
|
||||
t.Run("mark document as deleted", func(t *testing.T) {
|
||||
document := opensearchtest.Testdata.Resources.Full
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.ToJSON(t, document))
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.JSONMustMarshal(t, document))
|
||||
tc.Require.IndicesCount([]string{index}, "", 1)
|
||||
|
||||
tc.Require.IndicesCount([]string{index}, opensearch.NewRootQuery(
|
||||
@@ -133,7 +133,7 @@ func TestEngine_Restore(t *testing.T) {
|
||||
t.Run("mark document as not deleted", func(t *testing.T) {
|
||||
document := opensearchtest.Testdata.Resources.Full
|
||||
document.Deleted = true
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.ToJSON(t, document))
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.JSONMustMarshal(t, document))
|
||||
tc.Require.IndicesCount([]string{index}, "", 1)
|
||||
|
||||
tc.Require.IndicesCount([]string{index}, opensearch.NewRootQuery(
|
||||
@@ -160,7 +160,7 @@ func TestEngine_Purge(t *testing.T) {
|
||||
|
||||
t.Run("purge with full document", func(t *testing.T) {
|
||||
document := opensearchtest.Testdata.Resources.Full
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.ToJSON(t, document))
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.JSONMustMarshal(t, document))
|
||||
tc.Require.IndicesCount([]string{index}, "", 1)
|
||||
|
||||
require.NoError(t, engine.Purge(document.ID))
|
||||
@@ -182,14 +182,14 @@ func TestEngine_DocCount(t *testing.T) {
|
||||
|
||||
t.Run("ignore deleted documents", func(t *testing.T) {
|
||||
document := opensearchtest.Testdata.Resources.Full
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.ToJSON(t, document))
|
||||
tc.Require.DocumentCreate(index, document.ID, opensearchtest.JSONMustMarshal(t, document))
|
||||
tc.Require.IndicesCount([]string{index}, "", 1)
|
||||
|
||||
count, err := engine.DocCount()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), count)
|
||||
|
||||
tc.Require.Update(index, document.ID, opensearchtest.ToJSON(t, map[string]any{
|
||||
tc.Require.Update(index, document.ID, opensearchtest.JSONMustMarshal(t, map[string]any{
|
||||
"doc": map[string]any{
|
||||
"Deleted": true,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package opensearchtest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var TimeMustParse = func(t *testing.T, ts string) time.Time {
|
||||
tp, err := time.Parse(time.RFC3339Nano, ts)
|
||||
require.NoError(t, err, "failed to parse time %s", ts)
|
||||
|
||||
return tp
|
||||
}
|
||||
|
||||
func JSONMustMarshal(t *testing.T, data any) string {
|
||||
jsonData, err := json.Marshal(data)
|
||||
require.NoError(t, err, "failed to marshal data to JSON")
|
||||
return string(jsonData)
|
||||
}
|
||||
@@ -1,21 +1,8 @@
|
||||
package opensearchtest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type TableTest[G any, W any] struct {
|
||||
Name string
|
||||
Got G
|
||||
Want W
|
||||
Err error
|
||||
}
|
||||
|
||||
func ToJSON(t *testing.T, data any) string {
|
||||
jsonData, err := json.Marshal(data)
|
||||
require.NoError(t, err, "failed to marshal data to JSON")
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/ast"
|
||||
"github.com/opencloud-eu/opencloud/pkg/kql"
|
||||
@@ -133,6 +134,27 @@ func (k *KQL) getBuilder(node ast.Node) (Builder, error) {
|
||||
default:
|
||||
builder = NewMatchPhraseQuery(k.getFieldName(node.Key)).Query(node.Value)
|
||||
}
|
||||
case *ast.DateTimeNode:
|
||||
if node.Operator == nil {
|
||||
return builder, fmt.Errorf("date time node without operator: %w", ErrUnsupportedNodeType)
|
||||
}
|
||||
|
||||
q := NewRangeQuery[time.Time](k.getFieldName(node.Key))
|
||||
|
||||
switch node.Operator.Value {
|
||||
case ">":
|
||||
q.Gt(node.Value)
|
||||
case ">=":
|
||||
q.Gte(node.Value)
|
||||
case "<":
|
||||
q.Lt(node.Value)
|
||||
case "<=":
|
||||
q.Lte(node.Value)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported operator %s for date time node: %w", node.Operator.Value, ErrUnsupportedNodeType)
|
||||
}
|
||||
|
||||
return q, nil
|
||||
case *ast.GroupNode:
|
||||
group, err := k.compile(node.Nodes)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package opensearch_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -85,6 +86,58 @@ func TestKQL_Compile(t *testing.T) {
|
||||
},
|
||||
Want: opensearch.NewTermQuery[string]("Name").Value("any"),
|
||||
},
|
||||
{
|
||||
Name: "range query >",
|
||||
Got: &ast.Ast{
|
||||
Nodes: []ast.Node{
|
||||
&ast.DateTimeNode{
|
||||
Key: "Mtime",
|
||||
Operator: &ast.OperatorNode{Value: ">"},
|
||||
Value: opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Want: opensearch.NewRangeQuery[time.Time]("Mtime").Gt(opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00")),
|
||||
},
|
||||
{
|
||||
Name: "range query >=",
|
||||
Got: &ast.Ast{
|
||||
Nodes: []ast.Node{
|
||||
&ast.DateTimeNode{
|
||||
Key: "Mtime",
|
||||
Operator: &ast.OperatorNode{Value: ">="},
|
||||
Value: opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Want: opensearch.NewRangeQuery[time.Time]("Mtime").Gte(opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00")),
|
||||
},
|
||||
{
|
||||
Name: "range query <",
|
||||
Got: &ast.Ast{
|
||||
Nodes: []ast.Node{
|
||||
&ast.DateTimeNode{
|
||||
Key: "Mtime",
|
||||
Operator: &ast.OperatorNode{Value: "<"},
|
||||
Value: opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Want: opensearch.NewRangeQuery[time.Time]("Mtime").Lt(opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00")),
|
||||
},
|
||||
{
|
||||
Name: "range query <=",
|
||||
Got: &ast.Ast{
|
||||
Nodes: []ast.Node{
|
||||
&ast.DateTimeNode{
|
||||
Key: "Mtime",
|
||||
Operator: &ast.OperatorNode{Value: "<="},
|
||||
Value: opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Want: opensearch.NewRangeQuery[time.Time]("Mtime").Lte(opensearchtest.TimeMustParse(t, "2023-09-05T08:42:11.23554+02:00")),
|
||||
},
|
||||
// kql to os dsl - structure tests
|
||||
{
|
||||
Name: "[*]",
|
||||
@@ -253,7 +306,7 @@ func TestKQL_Compile(t *testing.T) {
|
||||
dsl, err := compiler.Compile(test.Got)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, dsl))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, dsl))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestBoolQuery(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, test.Got))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestNewMatchPhraseQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, test.Got))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestIDsQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, test.Got))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestTermQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, test.Got))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestWildcardQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, test.Got))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, test.Got))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestBuilderToBoolQuery(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
assert.JSONEq(t, opensearchtest.ToJSON(t, test.Want), opensearchtest.ToJSON(t, opensearch.BuilderToBoolQuery(test.Got)))
|
||||
assert.JSONEq(t, opensearchtest.JSONMustMarshal(t, test.Want), opensearchtest.JSONMustMarshal(t, opensearch.BuilderToBoolQuery(test.Got)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user