Implement executing queries using API

This commit is contained in:
Pijus Kamandulis 2024-02-12 01:54:12 +02:00
parent bdf9970ce6
commit 88526dcdcc
6 changed files with 134 additions and 5 deletions

View File

@ -77,7 +77,14 @@ func DocumentsPost(c *gin.Context) {
} }
// TODO: Handle these {"query":"select c.id, c._self, c._rid, c._ts, [c[\"pk\"]] as _partitionKeyValue from c"} // TODO: Handle these {"query":"select c.id, c._self, c._rid, c._ts, [c[\"pk\"]] as _partitionKeyValue from c"}
GetAllDocuments(c) docs, status := repositories.ExecuteQueryDocuments(databaseId, collectionId, query.(string))
if status != repositories.StatusOk {
// TODO: Currently we return everything if the query fails
GetAllDocuments(c)
return
}
c.IndentedJSON(http.StatusOK, gin.H{"_rid": "", "Documents": docs, "_count": len(docs)})
return return
} }

View File

@ -0,0 +1,89 @@
package tests_test
import (
"context"
"encoding/json"
"fmt"
"reflect"
"testing"
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/pikami/cosmium/internal/repositories"
"github.com/stretchr/testify/assert"
)
func testCosmosQuery(t *testing.T, collectionClient *azcosmos.ContainerClient, query string, expectedData []map[string]interface{}) {
pager := collectionClient.NewQueryItemsPager(
query,
azcosmos.PartitionKey{},
&azcosmos.QueryOptions{})
context := context.TODO()
items := make([]map[string]interface{}, 0)
for pager.More() {
response, err := pager.NextPage(context)
assert.Nil(t, err)
for _, bytes := range response.Items {
var item map[string]interface{}
err := json.Unmarshal(bytes, &item)
assert.Nil(t, err)
items = append(items, item)
}
}
assert.Equal(t, len(expectedData), len(items))
if !reflect.DeepEqual(items, expectedData) {
t.Errorf("executed query does not match expected data.\nExpected: %+v\nGot: %+v", expectedData, items)
}
}
func Test_Documents(t *testing.T) {
repositories.CreateCollection(testDatabaseName, repositories.Collection{
ID: testCollectionName,
PartitionKey: struct {
Paths []string "json:\"paths\""
Kind string "json:\"kind\""
Version int "json:\"Version\""
}{
Paths: []string{"/pk"},
},
})
repositories.CreateDocument(testDatabaseName, testCollectionName, map[string]interface{}{"id": "12345", "pk": "123", "isCool": false})
repositories.CreateDocument(testDatabaseName, testCollectionName, map[string]interface{}{"id": "67890", "pk": "456", "isCool": true})
ts := runTestServer()
defer ts.Close()
client, err := azcosmos.NewClientFromConnectionString(
fmt.Sprintf("AccountEndpoint=%s;AccountKey=%s", ts.URL, "asas"),
&azcosmos.ClientOptions{},
)
assert.Nil(t, err)
collectionClient, err := client.NewContainer(testDatabaseName, testCollectionName)
assert.Nil(t, err)
t.Run("Should query document", func(t *testing.T) {
testCosmosQuery(t, collectionClient,
"SELECT c.id, c[\"pk\"] FROM c",
[]map[string]interface{}{
{"id": "12345", "pk": "123"},
{"id": "67890", "pk": "456"},
},
)
})
t.Run("Should query document with single WHERE condition", func(t *testing.T) {
testCosmosQuery(t, collectionClient,
`select c.id
FROM c
WHERE c.isCool=true`,
[]map[string]interface{}{
{"id": "67890"},
},
)
})
}

View File

@ -1,6 +1,13 @@
package repositories package repositories
import "strings" import (
"log"
"strings"
"github.com/pikami/cosmium/parsers"
"github.com/pikami/cosmium/parsers/nosql"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
var documents = []Document{} var documents = []Document{}
@ -76,6 +83,12 @@ func CreateDocument(databaseId string, collectionId string, document map[string]
for _, part := range strings.Split(path, "/") { for _, part := range strings.Split(path, "/") {
val = document[part] val = document[part]
} }
if val == nil {
val = ""
}
// TODO: handle non-string partition keys
partitionKeyValue = append(partitionKeyValue, val.(string)) partitionKeyValue = append(partitionKeyValue, val.(string))
} }
@ -88,3 +101,23 @@ func CreateDocument(databaseId string, collectionId string, document map[string]
return StatusOk return StatusOk
} }
func ExecuteQueryDocuments(databaseId string, collectionId string, query string) ([]memoryexecutor.RowType, RepositoryStatus) {
parsedQuery, err := nosql.Parse("", []byte(query))
if err != nil {
log.Printf("Failed to parse query: %s\nerr: %v", query, err)
return nil, BadRequest
}
collectionDocuments, status := GetAllDocuments(databaseId, collectionId)
if status != StatusOk {
return nil, status
}
covDocs := make([]memoryexecutor.RowType, 0)
for _, doc := range collectionDocuments {
covDocs = append(covDocs, map[string]interface{}(doc))
}
return memoryexecutor.Execute(parsedQuery.(parsers.SelectStmt), covDocs), StatusOk
}

View File

@ -52,7 +52,7 @@ func Test_Parse(t *testing.T) {
) )
}) })
t.Run("Shoul 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,
`select c.id `select c.id

View File

@ -8,7 +8,7 @@ type RowType interface{}
type ExpressionType interface{} type ExpressionType interface{}
func Execute(query parsers.SelectStmt, data []RowType) []RowType { func Execute(query parsers.SelectStmt, data []RowType) []RowType {
var result []RowType result := make([]RowType, 0)
// Iterate over each row in the data // Iterate over each row in the data
for _, row := range data { for _, row := range data {

View File

@ -17,7 +17,7 @@ func testQueryExecute(
result := memoryexecutor.Execute(query, data) result := memoryexecutor.Execute(query, data)
if !reflect.DeepEqual(result, expectedData) { if !reflect.DeepEqual(result, expectedData) {
t.Errorf("parsed query does not match expected structure.\nExpected: %+v\nGot: %+v", expectedData, result) t.Errorf("execution result does not match expected data.\nExpected: %+v\nGot: %+v", expectedData, result)
} }
} }