From 79acc0efe53ff02dde7db2c2a406355d5f94009a Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 3 Nov 2017 11:50:03 -0700 Subject: [PATCH 1/3] Allow setting multiple fields in a single fset command. Add xx flag to fset. --- controller/collection/collection.go | 17 +++++++ controller/crud.go | 76 +++++++++++++++++------------ tests/keys_test.go | 7 ++- 3 files changed, 68 insertions(+), 32 deletions(-) diff --git a/controller/collection/collection.go b/controller/collection/collection.go index c3886c3e..55a955b0 100644 --- a/controller/collection/collection.go +++ b/controller/collection/collection.go @@ -233,6 +233,23 @@ func (c *Collection) SetField(id, field string, value float64) (obj geojson.Obje return item.object, c.getFieldValues(id), updated, true } +// SetFields is similar to SetField, just setting multiple fields at once +func (c *Collection) SetFields(id string, in_fields []string, in_values []float64) ( + obj geojson.Object, fields []float64, updated bool, ok bool, +) { + i := c.items.Get(&itemT{id: id}) + if i == nil { + ok = false + return + } + item := i.(*itemT) + for idx, field := range in_fields { + _upd := c.setField(item, field, in_values[idx]) + updated = updated || _upd + } + return item.object, c.getFieldValues(id), updated, true +} + func (c *Collection) setField(item *itemT, field string, value float64) (updated bool) { idx, ok := c.fieldMap[field] if !ok { diff --git a/controller/crud.go b/controller/crud.go index cabde995..7139082e 100644 --- a/controller/crud.go +++ b/controller/crud.go @@ -780,8 +780,9 @@ notok: return } -func (c *Controller) parseFSetArgs(vs []resp.Value) (d commandDetailsT, err error) { - var svalue string +func (c *Controller) parseFSetArgs(vs []resp.Value) ( + d commandDetailsT, fields []string, values []float64, xx bool, err error, +) { var ok bool if vs, d.key, ok = tokenval(vs); !ok || d.key == "" { err = errInvalidNumberOfArguments @@ -791,26 +792,33 @@ func (c *Controller) parseFSetArgs(vs []resp.Value) (d commandDetailsT, err erro err = errInvalidNumberOfArguments return } - if vs, d.field, ok = tokenval(vs); !ok || d.field == "" { - err = errInvalidNumberOfArguments - return - } - if isReservedFieldName(d.field) { - err = errInvalidNumberOfArguments - return - } - if vs, svalue, ok = tokenval(vs); !ok || svalue == "" { - err = errInvalidNumberOfArguments - return - } - if len(vs) != 0 { - err = errInvalidNumberOfArguments - return - } - d.value, err = strconv.ParseFloat(svalue, 64) - if err != nil { - err = errInvalidArgument(svalue) - return + for len(vs) > 0 { + var name string + if vs, name, ok = tokenval(vs); !ok || name == "" { + err = errInvalidNumberOfArguments + return + } + if lc(name, "xx") { + xx = true + continue + } + if isReservedFieldName(name) { + err = errInvalidArgument(name) + return + } + var svalue string + var value float64 + if vs, svalue, ok = tokenval(vs); !ok || svalue == "" { + err = errInvalidNumberOfArguments + return + } + value, err = strconv.ParseFloat(svalue, 64) + if err != nil { + err = errInvalidArgument(svalue) + return + } + fields = append(fields, name) + values = append(values, value) } return } @@ -818,24 +826,30 @@ func (c *Controller) parseFSetArgs(vs []resp.Value) (d commandDetailsT, err erro func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDetailsT, err error) { start := time.Now() vs := msg.Values[1:] - d, err = c.parseFSetArgs(vs) + var fields []string + var values []float64 + var xx bool + d, fields, values, xx, err = c.parseFSetArgs(vs) + col := c.getCol(d.key) if col == nil { err = errKeyNotFound return } var ok bool - d.obj, d.fields, d.updated, ok = col.SetField(d.id, d.field, d.value) - if !ok { + d.obj, d.fields, d.updated, ok = col.SetFields(d.id, fields, values) + if !(ok || xx) { err = errIDNotFound return } - d.command = "fset" - d.timestamp = time.Now() - fmap := col.FieldMap() - d.fmap = make(map[string]int) - for key, idx := range fmap { - d.fmap[key] = idx + if ok { + d.command = "fset" + d.timestamp = time.Now() + fmap := col.FieldMap() + d.fmap = make(map[string]int) + for key, idx := range fmap { + d.fmap[key] = idx + } } switch msg.OutputType { diff --git a/tests/keys_test.go b/tests/keys_test.go index d9d24f5d..b62fa621 100644 --- a/tests/keys_test.go +++ b/tests/keys_test.go @@ -80,9 +80,14 @@ func keys_FSET_test(mc *mockServer) error { {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7]"}, {"FSET", "mykey", "myid", "f1", 105.6}, {1}, {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f1 105.6]]"}, + {"FSET", "mykey", "myid", "f1", 1.1, "f2", 2.2}, {1}, + {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f1 1.1 f2 2.2]]"}, {"FSET", "mykey", "myid", "f1", 0}, {1}, + {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f2 2.2]]"}, + {"FSET", "mykey", "myid", "f2", 0}, {1}, {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7]"}, - {"FSET", "mykey", "myid", "f1", 0}, {0}, + {"FSET", "mykey", "myid2", "xx", "f1", 1.1, "f2", 2.2}, {0}, + {"GET", "mykey", "myid2"}, {nil}, {"DEL", "mykey", "myid"}, {"1"}, {"GET", "mykey", "myid"}, {nil}, }) From a3bf8b65728cbde18a4c2f071399529e5bac4539 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 10 Nov 2017 14:31:38 -0800 Subject: [PATCH 2/3] Tweak multi-field fset to return the count of updated fields --- controller/collection/collection.go | 9 +++++---- controller/crud.go | 10 ++++------ tests/keys_test.go | 6 ++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/controller/collection/collection.go b/controller/collection/collection.go index 55a955b0..198a4fa1 100644 --- a/controller/collection/collection.go +++ b/controller/collection/collection.go @@ -235,7 +235,7 @@ func (c *Collection) SetField(id, field string, value float64) (obj geojson.Obje // SetFields is similar to SetField, just setting multiple fields at once func (c *Collection) SetFields(id string, in_fields []string, in_values []float64) ( - obj geojson.Object, fields []float64, updated bool, ok bool, + obj geojson.Object, fields []float64, updated_count int, ok bool, ) { i := c.items.Get(&itemT{id: id}) if i == nil { @@ -244,10 +244,11 @@ func (c *Collection) SetFields(id string, in_fields []string, in_values []float6 } item := i.(*itemT) for idx, field := range in_fields { - _upd := c.setField(item, field, in_values[idx]) - updated = updated || _upd + if c.setField(item, field, in_values[idx]) { + updated_count++ + } } - return item.object, c.getFieldValues(id), updated, true + return item.object, c.getFieldValues(id), updated_count, true } func (c *Collection) setField(item *itemT, field string, value float64) (updated bool) { diff --git a/controller/crud.go b/controller/crud.go index 7139082e..1467ed1a 100644 --- a/controller/crud.go +++ b/controller/crud.go @@ -829,6 +829,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta var fields []string var values []float64 var xx bool + var updated_count int d, fields, values, xx, err = c.parseFSetArgs(vs) col := c.getCol(d.key) @@ -837,7 +838,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta return } var ok bool - d.obj, d.fields, d.updated, ok = col.SetFields(d.id, fields, values) + d.obj, d.fields, updated_count, ok = col.SetFields(d.id, fields, values) if !(ok || xx) { err = errIDNotFound return @@ -845,6 +846,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta if ok { d.command = "fset" d.timestamp = time.Now() + d.updated = updated_count > 0 fmap := col.FieldMap() d.fmap = make(map[string]int) for key, idx := range fmap { @@ -856,11 +858,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta case server.JSON: res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Now().Sub(start).String() + "\"}") case server.RESP: - if d.updated { - res = resp.IntegerValue(1) - } else { - res = resp.IntegerValue(0) - } + res = resp.IntegerValue(updated_count) } return } diff --git a/tests/keys_test.go b/tests/keys_test.go index b62fa621..7160a922 100644 --- a/tests/keys_test.go +++ b/tests/keys_test.go @@ -80,10 +80,12 @@ func keys_FSET_test(mc *mockServer) error { {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7]"}, {"FSET", "mykey", "myid", "f1", 105.6}, {1}, {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f1 105.6]]"}, - {"FSET", "mykey", "myid", "f1", 1.1, "f2", 2.2}, {1}, + {"FSET", "mykey", "myid", "f1", 1.1, "f2", 2.2}, {2}, {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f1 1.1 f2 2.2]]"}, + {"FSET", "mykey", "myid", "f1", 1.1, "f2", 22.22}, {1}, + {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f1 1.1 f2 22.22]]"}, {"FSET", "mykey", "myid", "f1", 0}, {1}, - {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f2 2.2]]"}, + {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7 [f2 22.22]]"}, {"FSET", "mykey", "myid", "f2", 0}, {1}, {"GET", "mykey", "myid", "WITHFIELDS", "HASH", 7}, {"[9my5xp7]"}, {"FSET", "mykey", "myid2", "xx", "f1", 1.1, "f2", 2.2}, {0}, From 2a5e760cee1be6f2c1854d3ccb5b088b44cde1d3 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 10 Nov 2017 14:58:51 -0800 Subject: [PATCH 3/3] Update commands.json for new fset --- core/commands.json | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/core/commands.json b/core/commands.json index 4cd045e5..2c8789cb 100644 --- a/core/commands.json +++ b/core/commands.json @@ -165,7 +165,7 @@ "group": "keys" }, "FSET": { - "summary": "Set the value for a single field of an id", + "summary": "Set the value for one or more fields of an id", "complexity": "O(1)", "arguments":[ { @@ -177,12 +177,15 @@ "type": "string" }, { - "name": "field", - "type": "string" + "command": "XX", + "name": [], + "type": [], + "optional": true }, { - "name": "value", - "type": "double" + "name": ["field","value"], + "type": ["string","double"], + "multiple": true } ], "since": "1.0.0", @@ -499,12 +502,12 @@ "type": "pattern", "optional": true }, - { - "command": "DISTANCE", - "name": [], - "type": [], - "optional": true - }, + { + "command": "DISTANCE", + "name": [], + "type": [], + "optional": true + }, { "command": "WHERE", "name": ["field","min","max"],