Skip to content

Commit

Permalink
Merge branch 'new-views' into skiplist-volume
Browse files Browse the repository at this point in the history
* new-views: (81 commits)
  Minor typo fix
  fixed spec for Time encoding (microsecond issue)
  added class Views as a wrapper for View[name]
  fixed random bug with random uuids sorting, added specs for :limit & :offset
  spec for view.find :key => ... added [WARNING! random bug detected: 20% cases -> wrong results order]
  updated views.txt
  spec for view insertion & find
  fixed comments for default_key_encoder
  default_key_encoder: Time handling
  removed stupid comments
  default_key_encoder: added Document decoding: returns raw uuid
  view.rb: changed API from View.find_or_create to View.define
  dropped :on_duplicate_key option
  changed ViewStorage#find to work with SimpleSkiplist#search
  view_storage: fixed @skiplist var name
  view_storage.rb: renamed @skiplist -> @list
  added specs for all SimpleSckiplist#search options except :reverse
  removed SearchSkiplist in favor of SimpleSkiplist
  moved stuff from SearchSkiplist to SimpleSkiplist
  view.rb: #update_head handles situation when there is no previous version
  ...
  • Loading branch information
Oleg Andreev committed Apr 28, 2008
2 parents 0c7c1ec + 56e88cc commit 23704a1
Show file tree
Hide file tree
Showing 18 changed files with 1,207 additions and 341 deletions.
4 changes: 2 additions & 2 deletions lib/strokedb.rb
Expand Up @@ -77,6 +77,6 @@ class NoDefaultStoreError < Exception ; end
require 'strokedb/volumes'
require 'strokedb/sync'
require 'strokedb/index'
require 'strokedb/view'
require 'strokedb/transaction'
require 'strokedb/stores'
require 'strokedb/stores'
require 'strokedb/views'
2 changes: 1 addition & 1 deletion lib/strokedb/data_structures.rb
Expand Up @@ -2,4 +2,4 @@
require 'data_structures/skiplist'
require 'data_structures/chunked_skiplist'
require 'data_structures/inverted_list'
require 'data_structures/point_query'
require 'data_structures/point_query'
73 changes: 73 additions & 0 deletions lib/strokedb/data_structures/simple_skiplist.rb
Expand Up @@ -43,6 +43,75 @@ def empty?
!node_next(@head, 0)
end

# Complicated search algorithm
# TODO: add reverse support
# TODO: add key duplication support
#
def search(start_key, end_key, limit, offset, reverse, with_keys)
offset ||= 0

start_node = find_by_prefix(start_key, reverse)
!start_node and return []

start_node = skip_nodes(start_node, offset, reverse)
!start_node and return []

collect_values(start_node, end_key, limit, reverse, with_keys)
end

#
#
def find_by_prefix(start_key, reverse)
# TODO: add reverse support
x = node_first # head [FIXME: change method name]
# if no prefix given, just return a first node
!start_key and return node_next(x, 0)

level = node_level(x)
while level > 0
level -= 1
xnext = node_next(x, level)
while node_compare(xnext, start_key) < 0
x = xnext
xnext = node_next(x, level)
end
end
xnext == @tail and return nil
node_key(xnext)[0, start_key.size] != start_key and return nil
xnext
end

#
#
def skip_nodes(node, offset, reverse)
# TODO: add reverse support
tail = @tail
while offset > 0 && node != tail
node = node_next(node, 0)
offset -= 1
end
offset <= 0 ? node : nil
end

#
#
def collect_values(x, end_prefix, limit, reverse, with_keys)
# TODO: add reverse support
values = []
meth = method(with_keys ? :node_pair : :node_value)
tail = @tail
limit ||= Float::MAX
end_prefix ||= ""
pfx_size = end_prefix.size
while x != tail
node_key(x)[0, pfx_size] > end_prefix and return values
values.size >= limit and return values
values << meth.call(x).freeze
x = node_next(x, 0)
end
values
end

# First key of a non-empty skiplist (nil for empty one)
#
def first_key
Expand Down Expand Up @@ -310,6 +379,10 @@ def node_compare(x, key)
x[1] <=> key
end

def node_pair(x)
x[1,2]
end

def node_key(x)
x[1]
end
Expand Down
2 changes: 1 addition & 1 deletion lib/strokedb/document/meta.rb
Expand Up @@ -186,7 +186,7 @@ def find(*args)
end

#
# Conveniance alias for Meta#find.
# Convenient alias for Meta#find.
#
alias :all :find

Expand Down
55 changes: 0 additions & 55 deletions lib/strokedb/view.rb

This file was deleted.

4 changes: 4 additions & 0 deletions lib/strokedb/views.rb
@@ -0,0 +1,4 @@
require 'views/view'
require 'views/default_key_encoder'
require 'views/raw_data_meta'
require 'views/view_storage'
137 changes: 137 additions & 0 deletions lib/strokedb/views/default_key_encoder.rb
@@ -0,0 +1,137 @@
module StrokeDB
module CoreExtensions
class ::NilClass
def default_key_encode; "A"; end
end
class ::FalseClass
def default_key_encode; "B"; end
end
class ::TrueClass
def default_key_encode; "C"; end
end
class ::Integer
# This helps with natural sort order
# "D<sign><number length (8 hex bytes)><hex>"
def default_key_encode
hex = self.abs.to_s(16)
if self >= 0
"D1" + [ hex.size ].pack("N").unpack("H*")[0] + hex
else
s = hex.size
"D0" + [ 2**32 - s ].pack("N").unpack("H*")[0] + (16**s + self).to_s(16)
end
end
end
class ::Float
# Encodes integer part and appends ratio part
# "D<sign><number length (4 bytes)><hex>.<dec>"
def default_key_encode
i = self.floor
r = self - i
i.default_key_encode + r.to_s[1, 666]
end
end
class ::String
STROKEDB_SPACE_CHAR = " ".freeze
STROKEDB_KEY_CHAR = "S".freeze
def default_key_encode
if self[STROKEDB_SPACE_CHAR]
split(STROKEDB_SPACE_CHAR).default_key_encode
else
STROKEDB_KEY_CHAR + self
end
end
end
class ::Symbol
def default_key_encode
to_s.default_key_encode
end
end
class ::Array
def default_key_encode
flatten.map{|e| e.default_key_encode }.join(" ")
end
end
class ::Hash
# Keys order is undefined, so just don't use this method.
def default_key_encode
raise(StandardError, "Hash cannot be used as a key! Please set up custom " +
"#encode_key method if you really need to.")
end
end
class ::Time
# Index key is in UTC format to provide correct sorting, but lacks timezone info.
# slot.rb maintains timezone offset and keeps timezone-local time value
STROKEDB_KEY_CHAR = "T".freeze
def default_key_encode
STROKEDB_KEY_CHAR + getgm.xmlschema(7)
end
end
end

class Document
AT_SIGN = "@".freeze
def default_key_encode
AT_SIGN + uuid
end
end

module DefaultKeyEncoder

# nil -> "A"
# false -> "B"
# true -> "C"
# Number -> "D<sign><number bitlength (8 hex bytes)><integer>[.<decimal>]"
# String -> "S<string>"
# Time -> "T<xmlschema>"
# Array -> "<elem1 elem2 ...>"
# Document -> "@<UUID>"
#
def self.encode(json)
json.default_key_encode
end

A = "A".freeze
B = "B".freeze
C = "C".freeze
D = "D".freeze
S = "S".freeze
T = "T".freeze
X = "@".freeze
S_= " ".freeze
R = (1..-1).freeze

def self.decode(string)
values = string.split(S_).map do |token|
pfx = token[0,1]
case pfx
when A
nil
when B
false
when C
true
when D
int, rat = token[10, 666].split(".")
sign = token[1, 1] == "1" ? 1 : -1
size = token[2, 8].to_i(16)
int = int.to_i(16)
if sign == -1
size = 2**32 - size
int = 16**size - int
end
rat ? sign*int + ("0."+rat).to_f : sign*int
when S
token[R]
when X
token[R]
when T
Time.xmlschema(token[R]).localtime
else
token # unknown stuff is decoded as a string
end
end
values.size > 1 ? values : values[0]
end
end
end
9 changes: 9 additions & 0 deletions lib/strokedb/views/raw_data_meta.rb
@@ -0,0 +1,9 @@
module StrokeDB

RawData = Meta.new(:nsurl => STROKEDB_NSURL)

def RawData(data)
RawData.new(:data => data)
end

end

0 comments on commit 23704a1

Please sign in to comment.