1
0
mirror of https://github.com/rancher/steve.git synced 2025-08-31 23:20:56 +00:00

Generate field names with brackets when needed. (#477)

* Generate field names with brackets when needed.

* Stop hard-wiring complex selectors as `["field1", "field2[sub-field3]"]`

and instead represent them as a more consistent `["field1", "field2", "sub-field3"]`

* Convert all filter strings ending with square brackets to an array.

Stop special-casing 'metadata.labels[X]' and handle any query string that ends with '[...]'.

* Stop checking for pre-bracketed terms in constant field-accessor arrays.

In this commit we stop converting string arrays like
`["metadata", "labels[k8s.io/deepcode]"]` into the database field
`"metadata.labels[k8s.io/deepcode]"` and instead will do a
naive `join` to give `metadata[labels[k8s.io/deepcode]]`.  The solution
is to never express the above terms in separate fields, like
`["metadata", "labels", "k8s.io/deepcode"]`. This also better reflects
the stucture of the referenced object.

* gofmt changes

* Simplify comment about 'smartJoin'.
This commit is contained in:
Eric Promislow
2025-02-25 10:39:29 -08:00
committed by GitHub
parent 527d44a3a7
commit 3b45729415
9 changed files with 228 additions and 49 deletions

View File

@@ -262,6 +262,7 @@ func TestListByOptions(t *testing.T) {
expectedCountStmtArgs []any
expectedStmt string
expectedStmtArgs []any
extraIndexedFields []string
expectedList *unstructured.UnstructuredList
returnList []any
expectedContToken string
@@ -688,6 +689,27 @@ func TestListByOptions(t *testing.T) {
expectedErr: nil,
})
tests = append(tests, testCase{
description: "ListByOptions sorting on two complex fields should sort on the first field in ascending order first and then sort on the second field in ascending order in prepared sql.Stmt",
listOptions: ListOptions{
Sort: Sort{
Fields: [][]string{{"metadata", "fields", "3"}, {"metadata", "labels", "stub.io/candy"}},
Orders: []SortOrder{ASC, ASC},
},
},
extraIndexedFields: []string{"metadata.fields[3]", "metadata.labels[stub.io/candy]"},
partitions: []partition.Partition{},
ns: "",
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
JOIN "something_fields" f ON o.key = f.key
WHERE
(FALSE)
ORDER BY f."metadata.fields[3]" ASC, f."metadata.labels[stub.io/candy]" ASC`,
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
expectedContToken: "",
expectedErr: nil,
})
tests = append(tests, testCase{
description: "ListByOptions sorting on two fields should sort on the first field in ascending order first and then sort on the second field in ascending order in prepared sql.Stmt",
listOptions: ListOptions{
@@ -909,8 +931,8 @@ func TestListByOptions(t *testing.T) {
Indexer: i,
indexedFields: []string{"metadata.somefield", "status.someotherfield"},
}
if test.description == "ListByOptions with multiple OrFilters set should select where all OrFilters contain one filter that is true in prepared sql.Stmt" {
fmt.Printf("stop here")
if len(test.extraIndexedFields) > 0 {
lii.indexedFields = append(lii.indexedFields, test.extraIndexedFields...)
}
queryInfo, err := lii.constructQuery(test.listOptions, test.partitions, test.ns, "something")
if test.expectedErr != nil {
@@ -1536,3 +1558,65 @@ func TestConstructQuery(t *testing.T) {
})
}
}
func TestSmartJoin(t *testing.T) {
type testCase struct {
description string
fieldArray []string
expectedFieldName string
}
var tests []testCase
tests = append(tests, testCase{
description: "single-letter names should be dotted",
fieldArray: []string{"metadata", "labels", "a"},
expectedFieldName: "metadata.labels.a",
})
tests = append(tests, testCase{
description: "underscore should be dotted",
fieldArray: []string{"metadata", "labels", "_"},
expectedFieldName: "metadata.labels._",
})
tests = append(tests, testCase{
description: "simple names should be dotted",
fieldArray: []string{"metadata", "labels", "queryField2"},
expectedFieldName: "metadata.labels.queryField2",
})
tests = append(tests, testCase{
description: "a numeric field should be bracketed",
fieldArray: []string{"metadata", "fields", "43"},
expectedFieldName: "metadata.fields[43]",
})
tests = append(tests, testCase{
description: "a field starting with a number should be bracketed",
fieldArray: []string{"metadata", "fields", "46days"},
expectedFieldName: "metadata.fields[46days]",
})
tests = append(tests, testCase{
description: "compound names should be bracketed",
fieldArray: []string{"metadata", "labels", "rancher.cattle.io/moo"},
expectedFieldName: "metadata.labels[rancher.cattle.io/moo]",
})
tests = append(tests, testCase{
description: "space-separated names should be bracketed",
fieldArray: []string{"metadata", "labels", "space here"},
expectedFieldName: "metadata.labels[space here]",
})
tests = append(tests, testCase{
description: "already-bracketed terms cause double-bracketing and should never be used",
fieldArray: []string{"metadata", "labels[k8s.io/deepcode]"},
expectedFieldName: "metadata[labels[k8s.io/deepcode]]",
})
tests = append(tests, testCase{
description: "an empty array should be an empty string",
fieldArray: []string{},
expectedFieldName: "",
})
t.Parallel()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
gotFieldName := smartJoin(test.fieldArray)
assert.Equal(t, test.expectedFieldName, gotFieldName)
})
}
}