Skip to content

Commit

Permalink
Merge pull request #30 from NoahShen/master
Browse files Browse the repository at this point in the history
Support regular expression query
  • Loading branch information
HouzuoGuo committed Nov 7, 2013
2 parents fc7d571 + ad93dc1 commit d76e5aa
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
48 changes: 48 additions & 0 deletions db/queryv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"regexp"
"strings"
)

Expand Down Expand Up @@ -321,6 +322,51 @@ func V2IntRange(intFrom interface{}, expr map[string]interface{}, src *Col, resu
return
}

// Execute value match regexp using hash lookup or collection scan.
func V2RegexpLookup(lookupRegexp interface{}, expr map[string]interface{}, src *Col, result *map[uint64]struct{}) (err error) {
// figure out lookup path - JSON array "in"
path, hasPath := expr["in"]
if !hasPath {
return errors.New("Missing lookup path `in`")
}
vecPath := make([]string, 0)
if vecPathInterface, ok := path.([]interface{}); ok {
for _, v := range vecPathInterface {
vecPath = append(vecPath, fmt.Sprint(v))
}
} else {
return errors.New(fmt.Sprintf("Expecting vector lookup path `in`, but %v given", path))
}
// figure out result number limit
intLimit := uint64(0)
if limit, hasLimit := expr["limit"]; hasLimit {
if floatLimit, ok := limit.(float64); ok {
intLimit = uint64(floatLimit)
} else {
return errors.New(fmt.Sprintf("Expecting `limit` as a number, but %v given", limit))
}
}
regexpStrValue := fmt.Sprint(lookupRegexp)
validRegexp := regexp.MustCompile(regexpStrValue)
// do collection scan
log.Printf("Query %v is a collection scan, which may be inefficient", expr)
counter := uint64(0)
docMatcher := func(id uint64, doc interface{}) bool {
// get inside the document and find value match
for _, v := range GetIn(doc, vecPath) {
if validRegexp.MatchString(fmt.Sprint(v)) {
(*result)[id] = struct{}{}
counter += 1
return counter != intLimit
}
}
return true
}
src.ForAll(docMatcher)

return
}

// Main entrance to query processor - evaluate a query and put result into result map (as map keys).
func EvalQueryV2(q interface{}, src *Col, result *map[uint64]struct{}) (err error) {
switch expr := q.(type) {
Expand All @@ -346,6 +392,8 @@ func EvalQueryV2(q interface{}, src *Col, result *map[uint64]struct{}) (err erro
return V2Complement(subExprs, src, result)
} else if intFrom, htRange := expr["int-from"]; htRange { // int-from, int-to - integer range query
return V2IntRange(intFrom, expr, src, result)
} else if lookupRegexp, lookup := expr["re"]; lookup {
return V2RegexpLookup(lookupRegexp, expr, src, result)
} else {
return errors.New(fmt.Sprintf("Query %v does not contain any operation (lookup/union/etc)", expr))
}
Expand Down
9 changes: 9 additions & 0 deletions db/queryv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,13 @@ func TestQueryV2(t *testing.T) {
if !ensureMapHasKeys(q, ids[4], ids[3]) {
t.Fatal(q)
}

q, err = runQueryV2(`{"re": "^[0-9]*$", "in": ["f"], "limit": 5}`, col)
if err != nil {
t.Fatal(err)
}
if !ensureMapHasKeys(q, ids[0], ids[1], ids[2], ids[3], ids[4]) {
fmt.Printf("%+v\n", q)
t.Fatal(q)
}
}

0 comments on commit d76e5aa

Please sign in to comment.