Skip to content

Commit

Permalink
Add GetLongestMatch API (#7)
Browse files Browse the repository at this point in the history
* fix(radix): disable export of size and root

* chore(readme): update readme

* feat(GetLongestMatch): add GetLongestMatch, fix an unit test issue
  • Loading branch information
ihexxa authored Nov 22, 2020
1 parent 7801ac3 commit 6758ed5
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ _A fast and simple (200+ lines) radix tree implementation in Go/Golang._
### Features

* good performance ([benchmark](https://github.com/ihexxa/radix-bench))
* more ways to query: GetAllMatches
* more ways to query (Get, GetAllMatches, GetLongestMatch, BFS)
* simple interfaces
* well tested (unit tests and random tests)

Expand All @@ -28,6 +28,7 @@ treeSize := rTree.Size() // get the size of the radix tree

val, ok := rTree.Get("key") // get the value by key
val, ok := rTree.GetAllMatches("key") // get all prefix matches of the key
val, ok := rTree.GetLongestMatch("key") // get the longest prefix match of the key

// override the value of the key if it exists in the radix tree
// and the old value will be returned if it exists in the radix tree
Expand Down
10 changes: 10 additions & 0 deletions radix.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ func (T *RTree) GetAllMatches(key string) []interface{} {
}
}

// GetLongestMatch returns the longest match in the tree according to the key
// if no match is found, it returns nil and false
func (T *RTree) GetLongestMatch(key string) (interface{}, bool) {
matches := T.GetAllMatches(key)
if len(matches) == 0 {
return nil, false
}
return matches[len(matches)-1], true
}

// split splits node into two nodes: parent and child.
// node1's prefix is [0, offset)
// node2's prefix is [offset, len-1]
Expand Down
67 changes: 65 additions & 2 deletions radix_unit_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package qradix

import "testing"
import (
"testing"
)

func TestOperations(t *testing.T) {
t.Run("test Insert", testInsert)
t.Run("test Remove", testRemove)
t.Run("test GetAllMatches", testGetAllMatches)
t.Run("test GetLongestMatch", testGetLongestMatch)
}

func testInsert(t *testing.T) {
Expand Down Expand Up @@ -67,7 +70,6 @@ func testRemove(t *testing.T) {
}

func testGetAllMatches(t *testing.T) {
rTree := NewRTree()
type TestCase struct {
desc string
inserts []string
Expand All @@ -88,9 +90,17 @@ func testGetAllMatches(t *testing.T) {
get: "a",
expect: []string{"a"},
},
&TestCase{
desc: "found a match shorter than key",
inserts: []string{"a", "ab", "abd"},
get: "abc",
expect: []string{"a", "ab"},
},
}

for _, tc := range testCases {
rTree := NewRTree()

for _, insert := range tc.inserts {
rTree.Insert(insert, insert)
}
Expand All @@ -103,3 +113,56 @@ func testGetAllMatches(t *testing.T) {
}
}
}

func testGetLongestMatch(t *testing.T) {
type TestCase struct {
desc string
inserts []string
get string
expect []string
}

testCases := []*TestCase{
&TestCase{
desc: "found a match as long as key",
inserts: []string{"a", "ab", "ac", "abc", "abcd"},
get: "abc",
expect: []string{"abc"},
},
&TestCase{
desc: "found a match shorter than key",
inserts: []string{"a", "ab", "abd"},
get: "abc",
expect: []string{"ab"},
},
&TestCase{
desc: "no match found",
inserts: []string{"a", "ab", "abd"},
get: "c",
expect: []string{},
},
}

for _, tc := range testCases {
rTree := NewRTree()

for _, insert := range tc.inserts {
rTree.Insert(insert, insert)
}

match, found := rTree.GetLongestMatch(tc.get)
if len(tc.expect) == 0 {
if found {
t.Errorf("GetLongestMatch(%s): expect no match but found one %s", tc.desc, match)
} else {
continue
}
} else {
if tc.expect[0] != match {
t.Errorf("GetLongestMatch(%s): got %s expect %s", tc.desc, match, tc.expect[0])
} else {
continue
}
}
}
}

0 comments on commit 6758ed5

Please sign in to comment.