Added support for array selects

This commit is contained in:
Pijus Kamandulis 2024-02-13 21:57:33 +02:00
parent e89f2e5611
commit 5d2b21dc46
7 changed files with 513 additions and 295 deletions

View File

@ -13,21 +13,21 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func testCosmosQuery(t *testing.T, collectionClient *azcosmos.ContainerClient, query string, expectedData []map[string]interface{}) { func testCosmosQuery(t *testing.T, collectionClient *azcosmos.ContainerClient, query string, expectedData []interface{}) {
pager := collectionClient.NewQueryItemsPager( pager := collectionClient.NewQueryItemsPager(
query, query,
azcosmos.PartitionKey{}, azcosmos.PartitionKey{},
&azcosmos.QueryOptions{}) &azcosmos.QueryOptions{})
context := context.TODO() context := context.TODO()
items := make([]map[string]interface{}, 0) items := make([]interface{}, 0)
for pager.More() { for pager.More() {
response, err := pager.NextPage(context) response, err := pager.NextPage(context)
assert.Nil(t, err) assert.Nil(t, err)
for _, bytes := range response.Items { for _, bytes := range response.Items {
var item map[string]interface{} var item interface{}
err := json.Unmarshal(bytes, &item) err := json.Unmarshal(bytes, &item)
assert.Nil(t, err) assert.Nil(t, err)
@ -70,9 +70,19 @@ func Test_Documents(t *testing.T) {
t.Run("Should query document", func(t *testing.T) { t.Run("Should query document", func(t *testing.T) {
testCosmosQuery(t, collectionClient, testCosmosQuery(t, collectionClient,
"SELECT c.id, c[\"pk\"] FROM c", "SELECT c.id, c[\"pk\"] FROM c",
[]map[string]interface{}{ []interface{}{
{"id": "12345", "pk": "123"}, map[string]interface{}{"id": "12345", "pk": "123"},
{"id": "67890", "pk": "456"}, map[string]interface{}{"id": "67890", "pk": "456"},
},
)
})
t.Run("Should query VALUE array", func(t *testing.T) {
testCosmosQuery(t, collectionClient,
"SELECT VALUE [c.id, c[\"pk\"]] FROM c",
[]interface{}{
[]interface{}{"12345", "123"},
[]interface{}{"67890", "456"},
}, },
) )
}) })
@ -82,8 +92,8 @@ func Test_Documents(t *testing.T) {
`select c.id `select c.id
FROM c FROM c
WHERE c.isCool=true`, WHERE c.isCool=true`,
[]map[string]interface{}{ []interface{}{
{"id": "67890"}, map[string]interface{}{"id": "67890"},
}, },
) )
}) })

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,19 @@ func makeColumnList(column interface{}, other_columns interface{}) ([]parsers.Se
return columnList, nil return columnList, nil
} }
func makeSelectArray(columns interface{}, asClause interface{}) (parsers.SelectItem, error) {
selectItem := parsers.SelectItem{
SelectItems: columns.([]parsers.SelectItem),
Type: parsers.SelectItemTypeArray,
}
if aliasValue, ok := asClause.(string); ok {
selectItem.Alias = aliasValue
}
return selectItem, nil
}
func joinStrings(array []interface{}) string { func joinStrings(array []interface{}) string {
var stringsArray []string var stringsArray []string
for _, elem := range array { for _, elem := range array {
@ -101,11 +114,17 @@ TableName <- key:Identifier {
return parsers.Table{Value: key.(string)}, nil return parsers.Table{Value: key.(string)}, nil
} }
SelectItem <- name:Identifier path:(DotFieldAccess / ArrayFieldAccess)* SelectArray <- "[" ws columns:ColumnList ws "]" asClause:AsClause? {
asClause:(ws "AS" ws alias:Identifier { return alias, nil })? { return makeSelectArray(columns, asClause)
}
SelectItem <- SelectArray / name:Identifier path:(DotFieldAccess / ArrayFieldAccess)*
asClause:AsClause? {
return makeSelectItem(name, path, asClause, parsers.SelectItemTypeField) return makeSelectItem(name, path, asClause, parsers.SelectItemTypeField)
} }
AsClause <- ws As ws alias:Identifier { return alias, nil }
DotFieldAccess <- "." id:Identifier { DotFieldAccess <- "." id:Identifier {
return id, nil return id, nil
} }
@ -136,6 +155,8 @@ ComparisonExpression <- left:(Literal / SelectItem) ws op:ComparisonOperator ws
Select <- ("select" / "SELECT") Select <- ("select" / "SELECT")
As <- ("as" / "AS")
From <- ("from" / "FROM") From <- ("from" / "FROM")
Where <- ("where" / "WHERE") Where <- ("where" / "WHERE")

View File

@ -65,6 +65,26 @@ func Test_Parse(t *testing.T) {
) )
}) })
t.Run("Should parse SELECT array", func(t *testing.T) {
testQueryParse(
t,
`SELECT [c.id, c.pk] as arr FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "arr",
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT with single WHERE condition", func(t *testing.T) { t.Run("Should parse SELECT with single WHERE condition", func(t *testing.T) {
testQueryParse( testQueryParse(
t, t,

View File

@ -84,6 +84,14 @@ func evaluateFilters(expr ExpressionType, row RowType) bool {
} }
func getFieldValue(field parsers.SelectItem, row RowType) interface{} { func getFieldValue(field parsers.SelectItem, row RowType) interface{} {
if field.Type == parsers.SelectItemTypeArray {
arrayValue := make([]interface{}, 0)
for _, selectItem := range field.SelectItems {
arrayValue = append(arrayValue, getFieldValue(selectItem, row))
}
return arrayValue
}
value := row value := row
for _, pathSegment := range field.Path[1:] { for _, pathSegment := range field.Path[1:] {
if nestedValue, ok := value.(map[string]interface{}); ok { if nestedValue, ok := value.(map[string]interface{}); ok {

View File

@ -62,6 +62,30 @@ func Test_Execute(t *testing.T) {
) )
}) })
t.Run("Should execute SELECT array", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "arr",
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"arr": []interface{}{"12345", 123}},
map[string]interface{}{"arr": []interface{}{"67890", 456}},
},
)
})
t.Run("Should execute SELECT with single WHERE condition", func(t *testing.T) { t.Run("Should execute SELECT with single WHERE condition", func(t *testing.T) {
testQueryExecute( testQueryExecute(
t, t,

24
tests.http Normal file
View File

@ -0,0 +1,24 @@
### Create collection
POST https://localhost:8081/dbs/db1/colls
Content-Type: application/json
{"id":"test1", "partitionKey":{"paths":["/pk"],"kind":"Hash","version":2}}
### Insert document
POST https://localhost:8081/dbs/db1/colls/test/docs
Content-Type: application/json
{"id":"1","_pk":"123","isCool":true}
### Get all documents
GET https://localhost:8081/dbs/db1/colls/test/docs
### Query documents
POST https://localhost:8081/dbs/db1/colls/test/docs
Content-Type: application/json
{"query":"SELECT c.id, c.isCool AS cool FROM c WHERE c.isCool = false"}