Skip to content

Commit

Permalink
#33 optimize XPath union operation(|) performance, 1000x times faster
Browse files Browse the repository at this point in the history
  • Loading branch information
zhengchun committed Mar 19, 2019
1 parent c8489ed commit ce1d487
Showing 1 changed file with 47 additions and 14 deletions.
61 changes: 47 additions & 14 deletions query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package xpath

import (
"bytes"
"fmt"
"hash/fnv"
"reflect"
)

Expand Down Expand Up @@ -728,35 +731,36 @@ type unionQuery struct {

func (u *unionQuery) Select(t iterator) NodeNavigator {
if u.iterator == nil {
var list []NodeNavigator
var i int
var m = make(map[uint64]NodeNavigator)
root := t.Current().Copy()
for {
node := u.Left.Select(t)
if node == nil {
break
}
node = node.Copy()
list = append(list, node)
code := getHashCode(node.Copy())
if _, ok := m[code]; !ok {
m[code] = node.Copy()
}
}
t.Current().MoveTo(root)
for {
node := u.Right.Select(t)
if node == nil {
break
}
node = node.Copy()
var exists bool
for _, x := range list {
if reflect.DeepEqual(x, node) {
exists = true
break
}
}
if !exists {
list = append(list, node)
code := getHashCode(node.Copy())
if _, ok := m[code]; !ok {
m[code] = node.Copy()
}
}
list := make([]NodeNavigator, len(m))
var i int
for _, v := range m {
list[i] = v
i++
}
i = 0
u.iterator = func() NodeNavigator {
if i >= len(list) {
return nil
Expand All @@ -780,6 +784,35 @@ func (u *unionQuery) Clone() query {
return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()}
}

func getHashCode(n NodeNavigator) uint64 {
var sb bytes.Buffer
switch n.NodeType() {
case AttributeNode, TextNode, CommentNode:
sb.WriteString(fmt.Sprintf("%s=%s", n.LocalName(), n.Value()))
if n.MoveToParent() {
sb.WriteString(n.LocalName())
}
case ElementNode:
sb.WriteString(n.Prefix() + n.LocalName())
d := 1
for n.MoveToPrevious() {
d++
}
sb.WriteString(fmt.Sprintf("-%d", d))

for n.MoveToParent() {
d = 1
for n.MoveToPrevious() {
d++
}
sb.WriteString(fmt.Sprintf("-%d", d))
}
}
h := fnv.New64a()
h.Write([]byte(sb.String()))
return h.Sum64()
}

func getNodePosition(q query) int {
type Position interface {
position() int
Expand Down

0 comments on commit ce1d487

Please sign in to comment.