Skip to content

Commit

Permalink
feat: Support for descending fields CLI index creation (sourcenetwork…
Browse files Browse the repository at this point in the history
…#3237)

## Relevant issue(s)

Resolves sourcenetwork#2460

## Description

Using the HTTP endpoint, it was possible to create a collection index,
with each field being either descending or ascending. This was possible
because one of the parameters was a boolean field called "Descending."
However, this functionality was not present in the CLI equivalent.

This feature adds support for creating descending fields.

For example, the following sorts name in ascending order, because
ascension is the default

`defradb client index create -c User --fields name`

The following sorts it in descending order:

`defradb client index create -c User --fields name:DESC`

And the following sorts multiple fields, in different combinations of
descending and ascending order:

`defradb client index create -c User --fields name:ASC,score:DESC`

## Tasks

- [x] I made sure the code is well commented, particularly
hard-to-understand areas.
- [x] I made sure the pull request title adheres to the conventional
commit style

## How has this been tested?

Specify the platform(s) on which this was tested:
- Windows
  • Loading branch information
ChrisBQu authored Nov 14, 2024
1 parent 332f9d6 commit 909c4af
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 9 deletions.
16 changes: 13 additions & 3 deletions cli/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import (
)

const (
errInvalidLensConfig string = "invalid lens configuration"
errSchemaVersionNotOfSchema string = "the given schema version is from a different schema"
errRequiredFlag string = "the required flag [--%s|-%s] is %s"
errInvalidLensConfig string = "invalid lens configuration"
errSchemaVersionNotOfSchema string = "the given schema version is from a different schema"
errRequiredFlag string = "the required flag [--%s|-%s] is %s"
errInvalidAscensionOrder string = "invalid order: expected ASC or DESC"
errInvalidInxedFieldDescription string = "invalid or malformed field description"
)

var (
Expand Down Expand Up @@ -55,3 +57,11 @@ func NewErrSchemaVersionNotOfSchema(schemaRoot string, schemaVersionID string) e
errors.NewKV("SchemaVersionID", schemaVersionID),
)
}

func NewErrInvalidAscensionOrder(fieldName string) error {
return errors.New(errInvalidAscensionOrder, errors.NewKV("Field", fieldName))
}

func NewErrInvalidInxedFieldDescription(fieldName string) error {
return errors.New(errInvalidInxedFieldDescription, errors.NewKV("Field", fieldName))
}
36 changes: 32 additions & 4 deletions cli/index_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
package cli

import (
"strings"

"github.com/spf13/cobra"

"github.com/sourcenetwork/defradb/client"
Expand All @@ -22,26 +24,51 @@ func MakeIndexCreateCommand() *cobra.Command {
var fieldsArg []string
var uniqueArg bool
var cmd = &cobra.Command{
Use: "create -c --collection <collection> --fields <fields> [-n --name <name>] [--unique]",
Use: "create -c --collection <collection> --fields <fields[:ASC|:DESC]> [-n --name <name>] [--unique]",
Short: "Creates a secondary index on a collection's field(s)",
Long: `Creates a secondary index on a collection's field(s).
The --name flag is optional. If not provided, a name will be generated automatically.
The --unique flag is optional. If provided, the index will be unique.
If no order is specified for the field, the default value will be "ASC"
Example: create an index for 'Users' collection on 'name' field:
defradb client index create --collection Users --fields name
Example: create a named index for 'Users' collection on 'name' field:
defradb client index create --collection Users --fields name --name UsersByName`,
defradb client index create --collection Users --fields name --name UsersByName
Example: create a unique index for 'Users' collection on 'name' in ascending order, and 'age' in descending order:
defradb client index create --collection Users --fields name:ASC,age:DESC --unique
`,
ValidArgs: []string{"collection", "fields", "name"},
RunE: func(cmd *cobra.Command, args []string) error {
store := mustGetContextStore(cmd)

var fields []client.IndexedFieldDescription
for _, name := range fieldsArg {
fields = append(fields, client.IndexedFieldDescription{Name: name})

for _, field := range fieldsArg {
// For each field, parse it into a field name and ascension order, separated by a colon
// If there is no colon, assume the ascension order is ASC by default
const asc = "ASC"
const desc = "DESC"
parts := strings.Split(field, ":")
fieldName := parts[0]
order := asc
if len(parts) == 2 {
order = strings.ToUpper(parts[1])
if order != asc && order != desc {
return NewErrInvalidAscensionOrder(field)
}
} else if len(parts) > 2 {
return NewErrInvalidInxedFieldDescription(field)
}
fields = append(fields, client.IndexedFieldDescription{
Name: fieldName,
Descending: order == desc,
})
}

desc := client.IndexDescription{
Name: nameArg,
Fields: fields,
Expand All @@ -51,6 +78,7 @@ Example: create a named index for 'Users' collection on 'name' field:
if err != nil {
return err
}

desc, err = col.CreateIndex(cmd.Context(), desc)
if err != nil {
return err
Expand Down
7 changes: 6 additions & 1 deletion docs/website/references/cli/defradb_client_index_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ Creates a secondary index on a collection's field(s).

The --name flag is optional. If not provided, a name will be generated automatically.
The --unique flag is optional. If provided, the index will be unique.
If no order is specified for the field, the default value will be "ASC"

Example: create an index for 'Users' collection on 'name' field:
defradb client index create --collection Users --fields name

Example: create a named index for 'Users' collection on 'name' field:
defradb client index create --collection Users --fields name --name UsersByName

Example: create a unique index for 'Users' collection on 'name' in ascending order, and 'age' in descending order:
defradb client index create --collection Users --fields name:ASC,age:DESC --unique


```
defradb client index create -c --collection <collection> --fields <fields> [-n --name <name>] [--unique] [flags]
defradb client index create -c --collection <collection> --fields <fields[:ASC|:DESC]> [-n --name <name>] [--unique] [flags]
```

### Options
Expand Down
16 changes: 15 additions & 1 deletion tests/clients/cli/wrapper_collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,24 @@ func (c *Collection) CreateIndex(
}

fields := make([]string, len(indexDesc.Fields))
orders := make([]bool, len(indexDesc.Fields))

for i := range indexDesc.Fields {
fields[i] = indexDesc.Fields[i].Name
orders[i] = indexDesc.Fields[i].Descending
}

orderedFields := make([]string, len(fields))

for i := range fields {
if orders[i] {
orderedFields[i] = fields[i] + ":DESC"
} else {
orderedFields[i] = fields[i] + ":ASC"
}
}
args = append(args, "--fields", strings.Join(fields, ","))

args = append(args, "--fields", strings.Join(orderedFields, ","))

data, err := c.cmd.execute(ctx, args)
if err != nil {
Expand Down
76 changes: 76 additions & 0 deletions tests/integration/index/create_unique_composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,79 @@ func TestUniqueCompositeIndexCreate_IfFieldValuesAreUnique_Succeed(t *testing.T)

testUtils.ExecuteTestCase(t, test)
}

func TestUniqueCompositeIndexCreate_IfFieldValuesAreOrdered_Succeed(t *testing.T) {
test := testUtils.TestCase{
Description: "create unique composite index if all docs have unique fields combinations",
Actions: []any{
testUtils.SchemaUpdate{
Schema: `
type User {
name: String
age: Int
email: String
}
`,
},
testUtils.CreateDoc{
CollectionID: 0,
Doc: `
{
"name": "John",
"age": 21,
"email": "[email protected]"
}`,
},
testUtils.CreateDoc{
CollectionID: 0,
Doc: `
{
"name": "John",
"age": 35,
"email": "[email protected]"
}`,
},
testUtils.CreateDoc{
CollectionID: 0,
Doc: `
{
"name": "Andy",
"age": 35,
"email": "[email protected]"
}`,
},
testUtils.CreateIndex{
CollectionID: 0,
Fields: []testUtils.IndexedField{{Name: "name", Descending: true}, {Name: "age", Descending: false}, {Name: "email"}},
IndexName: "name_age_unique_index",
Unique: true,
},
testUtils.GetIndexes{
CollectionID: 0,
ExpectedIndexes: []client.IndexDescription{
{
Name: "name_age_unique_index",
ID: 1,
Unique: true,
Fields: []client.IndexedFieldDescription{
{
Name: "name",
Descending: true,
},
{
Name: "age",
Descending: false,
},
{
Name: "email",
Descending: false,
},
},
},
},
},
},
}

testUtils.ExecuteTestCase(t, test)
}

0 comments on commit 909c4af

Please sign in to comment.