Allow for multiple MATCH patterns
Each MATCH is inclusive OR, thus WITHIN fleet MATCH train* truck* BOUNDS 33 -112 34 -113 will find all trains and trucks that within the provides bounds.
This commit is contained in:
parent
d2953307a0
commit
f24c251ee6
@ -55,6 +55,20 @@ func objIsSpatial(obj geojson.Object) bool {
|
|||||||
func hookJSONString(hookName string, metas []FenceMeta) string {
|
func hookJSONString(hookName string, metas []FenceMeta) string {
|
||||||
return string(appendHookDetails(nil, hookName, metas))
|
return string(appendHookDetails(nil, hookName, metas))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func multiGlobMatch(globs []string, s string) bool {
|
||||||
|
if len(globs) == 0 || (len(globs) == 1 && globs[0] == "*") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, pattern := range globs {
|
||||||
|
match, _ := glob.Match(pattern, s)
|
||||||
|
if match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func fenceMatch(
|
func fenceMatch(
|
||||||
hookName string, sw *scanWriter, fence *liveFenceSwitches,
|
hookName string, sw *scanWriter, fence *liveFenceSwitches,
|
||||||
metas []FenceMeta, details *commandDetails,
|
metas []FenceMeta, details *commandDetails,
|
||||||
@ -66,11 +80,8 @@ func fenceMatch(
|
|||||||
`,"time":` + jsonTimeFormat(details.timestamp) + `}`,
|
`,"time":` + jsonTimeFormat(details.timestamp) + `}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(fence.glob) > 0 && !(len(fence.glob) == 1 && fence.glob[0] == '*') {
|
if !multiGlobMatch(fence.globs, details.id) {
|
||||||
match, _ := glob.Match(fence.glob, details.id)
|
return nil
|
||||||
if !match {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if details.obj == nil || !objIsSpatial(details.obj) {
|
if details.obj == nil || !objIsSpatial(details.obj) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -143,7 +143,7 @@ func (s *Server) cmdSetHook(msg *Message) (
|
|||||||
}
|
}
|
||||||
var wr bytes.Buffer
|
var wr bytes.Buffer
|
||||||
hook.ScanWriter, err = s.newScanWriter(
|
hook.ScanWriter, err = s.newScanWriter(
|
||||||
&wr, cmsg, args.key, args.output, args.precision, args.glob, false,
|
&wr, cmsg, args.key, args.output, args.precision, args.globs, false,
|
||||||
args.cursor, args.limit, args.wheres, args.whereins, args.whereevals,
|
args.cursor, args.limit, args.wheres, args.whereins, args.whereevals,
|
||||||
args.nofields)
|
args.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
type liveBuffer struct {
|
type liveBuffer struct {
|
||||||
key string
|
key string
|
||||||
glob string
|
globs []string
|
||||||
fence *liveFenceSwitches
|
fence *liveFenceSwitches
|
||||||
details []*commandDetails
|
details []*commandDetails
|
||||||
cond *sync.Cond
|
cond *sync.Cond
|
||||||
@ -101,12 +101,12 @@ func (s *Server) goLive(
|
|||||||
var sw *scanWriter
|
var sw *scanWriter
|
||||||
var wr bytes.Buffer
|
var wr bytes.Buffer
|
||||||
lfs := inerr.(liveFenceSwitches)
|
lfs := inerr.(liveFenceSwitches)
|
||||||
lb.glob = lfs.glob
|
lb.globs = lfs.globs
|
||||||
lb.key = lfs.key
|
lb.key = lfs.key
|
||||||
lb.fence = &lfs
|
lb.fence = &lfs
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
sw, err = s.newScanWriter(
|
sw, err = s.newScanWriter(
|
||||||
&wr, msg, lfs.key, lfs.output, lfs.precision, lfs.glob, false,
|
&wr, msg, lfs.key, lfs.output, lfs.precision, lfs.globs, false,
|
||||||
lfs.cursor, lfs.limit, lfs.wheres, lfs.whereins, lfs.whereevals, lfs.nofields)
|
lfs.cursor, lfs.limit, lfs.wheres, lfs.whereins, lfs.whereevals, lfs.nofields)
|
||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/tidwall/geojson"
|
"github.com/tidwall/geojson"
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/internal/glob"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) cmdScanArgs(vs []string) (
|
func (s *Server) cmdScanArgs(vs []string) (
|
||||||
@ -46,7 +45,7 @@ func (s *Server) cmdScan(msg *Message) (res resp.Value, err error) {
|
|||||||
}
|
}
|
||||||
wr := &bytes.Buffer{}
|
wr := &bytes.Buffer{}
|
||||||
sw, err := s.newScanWriter(
|
sw, err := s.newScanWriter(
|
||||||
wr, msg, args.key, args.output, args.precision, args.glob, false,
|
wr, msg, args.key, args.output, args.precision, args.globs, false,
|
||||||
args.cursor, args.limit, args.wheres, args.whereins, args.whereevals,
|
args.cursor, args.limit, args.wheres, args.whereins, args.whereevals,
|
||||||
args.nofields)
|
args.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -65,8 +64,8 @@ func (s *Server) cmdScan(msg *Message) (res resp.Value, err error) {
|
|||||||
}
|
}
|
||||||
sw.count = uint64(count)
|
sw.count = uint64(count)
|
||||||
} else {
|
} else {
|
||||||
g := glob.Parse(sw.globPattern, args.desc)
|
limits := multiGlobParse(sw.globs, args.desc)
|
||||||
if g.Limits[0] == "" && g.Limits[1] == "" {
|
if limits[0] == "" && limits[1] == "" {
|
||||||
sw.col.Scan(args.desc, sw,
|
sw.col.Scan(args.desc, sw,
|
||||||
msg.Deadline,
|
msg.Deadline,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
@ -78,7 +77,7 @@ func (s *Server) cmdScan(msg *Message) (res resp.Value, err error) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
sw.col.ScanRange(g.Limits[0], g.Limits[1], args.desc, sw,
|
sw.col.ScanRange(limits[0], limits[1], args.desc, sw,
|
||||||
msg.Deadline,
|
msg.Deadline,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
|
@ -52,9 +52,8 @@ type scanWriter struct {
|
|||||||
once bool
|
once bool
|
||||||
count uint64
|
count uint64
|
||||||
precision uint64
|
precision uint64
|
||||||
globPattern string
|
globs []string
|
||||||
globEverything bool
|
globEverything bool
|
||||||
globSingle bool
|
|
||||||
fullFields bool
|
fullFields bool
|
||||||
values []resp.Value
|
values []resp.Value
|
||||||
matchValues bool
|
matchValues bool
|
||||||
@ -79,7 +78,7 @@ type ScanWriterParams struct {
|
|||||||
|
|
||||||
func (s *Server) newScanWriter(
|
func (s *Server) newScanWriter(
|
||||||
wr *bytes.Buffer, msg *Message, key string, output outputT,
|
wr *bytes.Buffer, msg *Message, key string, output outputT,
|
||||||
precision uint64, globPattern string, matchValues bool,
|
precision uint64, globs []string, matchValues bool,
|
||||||
cursor, limit uint64, wheres []whereT, whereins []whereinT,
|
cursor, limit uint64, wheres []whereT, whereins []whereinT,
|
||||||
whereevals []whereevalT, nofields bool,
|
whereevals []whereevalT, nofields bool,
|
||||||
) (
|
) (
|
||||||
@ -102,21 +101,18 @@ func (s *Server) newScanWriter(
|
|||||||
wr: wr,
|
wr: wr,
|
||||||
key: key,
|
key: key,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
|
globs: globs,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
cursor: cursor,
|
cursor: cursor,
|
||||||
output: output,
|
output: output,
|
||||||
nofields: nofields,
|
nofields: nofields,
|
||||||
precision: precision,
|
precision: precision,
|
||||||
whereevals: whereevals,
|
whereevals: whereevals,
|
||||||
globPattern: globPattern,
|
|
||||||
matchValues: matchValues,
|
matchValues: matchValues,
|
||||||
}
|
}
|
||||||
if globPattern == "*" || globPattern == "" {
|
|
||||||
|
if len(globs) == 0 || (len(globs) == 1 && globs[0] == "*") {
|
||||||
sw.globEverything = true
|
sw.globEverything = true
|
||||||
} else {
|
|
||||||
if !glob.IsGlob(globPattern) {
|
|
||||||
sw.globSingle = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sw.orgWheres = wheres
|
sw.orgWheres = wheres
|
||||||
sw.orgWhereins = whereins
|
sw.orgWhereins = whereins
|
||||||
@ -344,25 +340,23 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sw *scanWriter) globMatch(id string, o geojson.Object) (ok, keepGoing bool) {
|
func (sw *scanWriter) globMatch(id string, o geojson.Object) (ok, keepGoing bool) {
|
||||||
if !sw.globEverything {
|
if sw.globEverything {
|
||||||
if sw.globSingle {
|
return true, true
|
||||||
if sw.globPattern != id {
|
}
|
||||||
return false, true
|
var val string
|
||||||
}
|
if sw.matchValues {
|
||||||
return true, false
|
val = o.String()
|
||||||
}
|
} else {
|
||||||
var val string
|
val = id
|
||||||
if sw.matchValues {
|
}
|
||||||
val = o.String()
|
for _, pattern := range sw.globs {
|
||||||
} else {
|
ok, _ := glob.Match(pattern, val)
|
||||||
val = id
|
if ok {
|
||||||
}
|
return true, true
|
||||||
ok, _ := glob.Match(sw.globPattern, val)
|
|
||||||
if !ok {
|
|
||||||
return false, true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true, true
|
return false, true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment cursor
|
// Increment cursor
|
||||||
|
@ -488,7 +488,7 @@ func (s *Server) cmdNearby(msg *Message) (res resp.Value, err error) {
|
|||||||
return NOMessage, sargs
|
return NOMessage, sargs
|
||||||
}
|
}
|
||||||
sw, err := s.newScanWriter(
|
sw, err := s.newScanWriter(
|
||||||
wr, msg, sargs.key, sargs.output, sargs.precision, sargs.glob, false,
|
wr, msg, sargs.key, sargs.output, sargs.precision, sargs.globs, false,
|
||||||
sargs.cursor, sargs.limit, sargs.wheres, sargs.whereins, sargs.whereevals, sargs.nofields)
|
sargs.cursor, sargs.limit, sargs.wheres, sargs.whereins, sargs.whereevals, sargs.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NOMessage, err
|
return NOMessage, err
|
||||||
@ -580,7 +580,7 @@ func (s *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp.Value
|
|||||||
return NOMessage, sargs
|
return NOMessage, sargs
|
||||||
}
|
}
|
||||||
sw, err := s.newScanWriter(
|
sw, err := s.newScanWriter(
|
||||||
wr, msg, sargs.key, sargs.output, sargs.precision, sargs.glob, false,
|
wr, msg, sargs.key, sargs.output, sargs.precision, sargs.globs, false,
|
||||||
sargs.cursor, sargs.limit, sargs.wheres, sargs.whereins, sargs.whereevals, sargs.nofields)
|
sargs.cursor, sargs.limit, sargs.wheres, sargs.whereins, sargs.whereevals, sargs.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NOMessage, err
|
return NOMessage, err
|
||||||
@ -644,6 +644,35 @@ func (s *Server) cmdSeachValuesArgs(vs []string) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func multiGlobParse(globs []string, desc bool) [2]string {
|
||||||
|
var limits [2]string
|
||||||
|
for i, pattern := range globs {
|
||||||
|
g := glob.Parse(pattern, desc)
|
||||||
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
||||||
|
limits[0], limits[1] = "", ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
limits[0], limits[1] = g.Limits[0], g.Limits[1]
|
||||||
|
} else if desc {
|
||||||
|
if g.Limits[0] > limits[0] {
|
||||||
|
limits[0] = g.Limits[0]
|
||||||
|
}
|
||||||
|
if g.Limits[1] < limits[1] {
|
||||||
|
limits[1] = g.Limits[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if g.Limits[0] < limits[0] {
|
||||||
|
limits[0] = g.Limits[0]
|
||||||
|
}
|
||||||
|
if g.Limits[1] > limits[1] {
|
||||||
|
limits[1] = g.Limits[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return limits
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
func (s *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
vs := msg.Args[1:]
|
vs := msg.Args[1:]
|
||||||
@ -664,7 +693,7 @@ func (s *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
|||||||
return NOMessage, err
|
return NOMessage, err
|
||||||
}
|
}
|
||||||
sw, err := s.newScanWriter(
|
sw, err := s.newScanWriter(
|
||||||
wr, msg, sargs.key, sargs.output, sargs.precision, sargs.glob, true,
|
wr, msg, sargs.key, sargs.output, sargs.precision, sargs.globs, true,
|
||||||
sargs.cursor, sargs.limit, sargs.wheres, sargs.whereins, sargs.whereevals, sargs.nofields)
|
sargs.cursor, sargs.limit, sargs.wheres, sargs.whereins, sargs.whereevals, sargs.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NOMessage, err
|
return NOMessage, err
|
||||||
@ -681,8 +710,8 @@ func (s *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
|||||||
}
|
}
|
||||||
sw.count = uint64(count)
|
sw.count = uint64(count)
|
||||||
} else {
|
} else {
|
||||||
g := glob.Parse(sw.globPattern, sargs.desc)
|
limits := multiGlobParse(sw.globs, sargs.desc)
|
||||||
if g.Limits[0] == "" && g.Limits[1] == "" {
|
if limits[0] == "" && limits[1] == "" {
|
||||||
sw.col.SearchValues(sargs.desc, sw, msg.Deadline,
|
sw.col.SearchValues(sargs.desc, sw, msg.Deadline,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
@ -696,8 +725,7 @@ func (s *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
|||||||
} else {
|
} else {
|
||||||
// must disable globSingle for string value type matching because
|
// must disable globSingle for string value type matching because
|
||||||
// globSingle is only for ID matches, not values.
|
// globSingle is only for ID matches, not values.
|
||||||
sw.globSingle = false
|
sw.col.SearchValuesRange(limits[0], limits[1], sargs.desc, sw,
|
||||||
sw.col.SearchValuesRange(g.Limits[0], g.Limits[1], sargs.desc, sw,
|
|
||||||
msg.Deadline,
|
msg.Deadline,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
|
@ -200,7 +200,7 @@ type searchScanBaseTokens struct {
|
|||||||
nodwell bool
|
nodwell bool
|
||||||
detect map[string]bool
|
detect map[string]bool
|
||||||
accept map[string]bool
|
accept map[string]bool
|
||||||
glob string
|
globs []string
|
||||||
wheres []whereT
|
wheres []whereT
|
||||||
whereins []whereinT
|
whereins []whereinT
|
||||||
whereevals []whereevalT
|
whereevals []whereevalT
|
||||||
@ -543,14 +543,12 @@ func (s *Server) parseSearchScanBaseTokens(
|
|||||||
continue
|
continue
|
||||||
case "match":
|
case "match":
|
||||||
vs = nvs
|
vs = nvs
|
||||||
if t.glob != "" {
|
var glob string
|
||||||
err = errDuplicateArgument(strings.ToUpper(wtok))
|
if vs, glob, ok = tokenval(vs); !ok || glob == "" {
|
||||||
return
|
|
||||||
}
|
|
||||||
if vs, t.glob, ok = tokenval(vs); !ok || t.glob == "" {
|
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.globs = append(t.globs, glob)
|
||||||
continue
|
continue
|
||||||
case "clip":
|
case "clip":
|
||||||
vs = nvs
|
vs = nvs
|
||||||
|
@ -591,23 +591,28 @@ func keys_MATCH_test(mc *mockServer) error {
|
|||||||
{"SET", "fleet", "truck2", "POINT", "33.0002", "-112.0002"}, {"OK"},
|
{"SET", "fleet", "truck2", "POINT", "33.0002", "-112.0002"}, {"OK"},
|
||||||
{"SET", "fleet", "luck1", "POINT", "33.0003", "-112.0003"}, {"OK"},
|
{"SET", "fleet", "luck1", "POINT", "33.0003", "-112.0003"}, {"OK"},
|
||||||
{"SET", "fleet", "luck2", "POINT", "33.0004", "-112.0004"}, {"OK"},
|
{"SET", "fleet", "luck2", "POINT", "33.0004", "-112.0004"}, {"OK"},
|
||||||
|
{"SET", "fleet", "train1", "POINT", "33.0005", "-112.0005"}, {"OK"},
|
||||||
|
|
||||||
{"SCAN", "fleet", "IDS"}, {"[0 [luck1 luck2 truck1 truck2]]"},
|
{"SCAN", "fleet", "IDS"}, {"[0 [luck1 luck2 train1 truck1 truck2]]"},
|
||||||
{"SCAN", "fleet", "MATCH", "*", "IDS"}, {"[0 [luck1 luck2 truck1 truck2]]"},
|
{"SCAN", "fleet", "MATCH", "*", "IDS"}, {"[0 [luck1 luck2 train1 truck1 truck2]]"},
|
||||||
{"SCAN", "fleet", "MATCH", "truck*", "IDS"}, {"[0 [truck1 truck2]]"},
|
{"SCAN", "fleet", "MATCH", "truck*", "IDS"}, {"[0 [truck1 truck2]]"},
|
||||||
{"SCAN", "fleet", "MATCH", "luck*", "IDS"}, {"[0 [luck1 luck2]]"},
|
{"SCAN", "fleet", "MATCH", "luck*", "IDS"}, {"[0 [luck1 luck2]]"},
|
||||||
{"SCAN", "fleet", "MATCH", "*2", "IDS"}, {"[0 [luck2 truck2]]"},
|
{"SCAN", "fleet", "MATCH", "*2", "IDS"}, {"[0 [luck2 truck2]]"},
|
||||||
{"SCAN", "fleet", "MATCH", "*2*", "IDS"}, {"[0 [luck2 truck2]]"},
|
{"SCAN", "fleet", "MATCH", "*2*", "IDS"}, {"[0 [luck2 truck2]]"},
|
||||||
{"SCAN", "fleet", "MATCH", "*u*", "IDS"}, {"[0 [luck1 luck2 truck1 truck2]]"},
|
{"SCAN", "fleet", "MATCH", "*u*", "IDS"}, {"[0 [luck1 luck2 truck1 truck2]]"},
|
||||||
|
{"SCAN", "fleet", "MATCH", "*u*", "MATCH", "*u*", "IDS"}, {"[0 [luck1 luck2 truck1 truck2]]"},
|
||||||
|
{"SCAN", "fleet", "MATCH", "*u*", "MATCH", "*a*", "IDS"}, {"[0 [luck1 luck2 train1 truck1 truck2]]"},
|
||||||
|
{"SCAN", "fleet", "MATCH", "train*", "MATCH", "truck*", "IDS"}, {"[0 [train1 truck1 truck2]]"},
|
||||||
|
{"SCAN", "fleet", "MATCH", "train*", "MATCH", "truck*", "MATCH", "luck1", "IDS"}, {"[0 [luck1 train1 truck1 truck2]]"},
|
||||||
|
|
||||||
{"NEARBY", "fleet", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
{"NEARBY", "fleet", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
||||||
match("[0 [luck1 luck2 truck1 truck2]]"),
|
match("[0 [luck1 luck2 train1 truck1 truck2]]"),
|
||||||
},
|
},
|
||||||
{"NEARBY", "fleet", "MATCH", "*", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
{"NEARBY", "fleet", "MATCH", "*", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
||||||
match("[0 [luck1 luck2 truck1 truck2]]"),
|
match("[0 [luck1 luck2 train1 truck1 truck2]]"),
|
||||||
},
|
},
|
||||||
{"NEARBY", "fleet", "MATCH", "t*", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
{"NEARBY", "fleet", "MATCH", "t*", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
||||||
match("[0 [truck1 truck2]]"),
|
match("[0 [train1 truck1 truck2]]"),
|
||||||
},
|
},
|
||||||
{"NEARBY", "fleet", "MATCH", "t*2", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
{"NEARBY", "fleet", "MATCH", "t*2", "IDS", "POINT", 33.00005, -112.00005, 100000}, {
|
||||||
match("[0 [truck2]]"),
|
match("[0 [truck2]]"),
|
||||||
@ -617,13 +622,13 @@ func keys_MATCH_test(mc *mockServer) error {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{"INTERSECTS", "fleet", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
{"INTERSECTS", "fleet", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
||||||
match("[0 [luck1 luck2 truck1 truck2]]"),
|
match("[0 [luck1 luck2 train1 truck1 truck2]]"),
|
||||||
},
|
},
|
||||||
{"INTERSECTS", "fleet", "MATCH", "*", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
{"INTERSECTS", "fleet", "MATCH", "*", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
||||||
match("[0 [luck1 luck2 truck1 truck2]]"),
|
match("[0 [luck1 luck2 train1 truck1 truck2]]"),
|
||||||
},
|
},
|
||||||
{"INTERSECTS", "fleet", "MATCH", "t*", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
{"INTERSECTS", "fleet", "MATCH", "t*", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
||||||
match("[0 [truck1 truck2]]"),
|
match("[0 [train1 truck1 truck2]]"),
|
||||||
},
|
},
|
||||||
{"INTERSECTS", "fleet", "MATCH", "t*2", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
{"INTERSECTS", "fleet", "MATCH", "t*2", "IDS", "BOUNDS", 33, -113, 34, -112}, {
|
||||||
match("[0 [truck2]]"),
|
match("[0 [truck2]]"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user