flockdb 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/LICENSE +13 -0
- data/README.md +60 -0
- data/Rakefile +34 -0
- data/VERSION +1 -0
- data/flockdb.gemspec +82 -0
- data/lib/flock.rb +40 -0
- data/lib/flock/client.rb +124 -0
- data/lib/flock/mixins/sizeable.rb +17 -0
- data/lib/flock/mock_service.rb +178 -0
- data/lib/flock/operation.rb +116 -0
- data/lib/flock/operations/complex_operation.rb +15 -0
- data/lib/flock/operations/execute_operation.rb +14 -0
- data/lib/flock/operations/execute_operations.rb +35 -0
- data/lib/flock/operations/query_term.rb +25 -0
- data/lib/flock/operations/select_operation.rb +51 -0
- data/lib/flock/operations/simple_operation.rb +16 -0
- data/lib/flock/service.rb +16 -0
- data/lib/flock/thrift/edges.rb +1445 -0
- data/lib/flock/thrift/edges_types.rb +210 -0
- data/lib/flock/thrift/flock_constants.rb +12 -0
- data/lib/flock/thrift/flock_types.rb +168 -0
- data/lib/flock/thrift/shards.rb +1598 -0
- data/lib/flockdb.rb +1 -0
- data/spec/flock_spec.rb +102 -0
- data/spec/mock_service_spec.rb +27 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +19 -0
- metadata +145 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg/*
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
FlockDB Client is Copyright (C) 2010 Twitter, Inc.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
4
|
+
use this work except in compliance with the License. You may obtain a copy of
|
5
|
+
the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
11
|
+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
12
|
+
License for the specific language governing permissions and limitations under
|
13
|
+
the License.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
FlockDB ruby client
|
2
|
+
|
3
|
+
FlockDB is a distributed graph database capable of storing millions of
|
4
|
+
nodes, billions of edges, and handling tens of thousands of operations
|
5
|
+
per second. Interact with it in ruby with this client.
|
6
|
+
|
7
|
+
|
8
|
+
INSTALLATION
|
9
|
+
------------
|
10
|
+
|
11
|
+
gem install flockdb
|
12
|
+
|
13
|
+
FlockDB depends on thrift and thrift_client, which will install as
|
14
|
+
part of the gem's dependencies. Note that thrift has a native C
|
15
|
+
extension which it will need to compile.
|
16
|
+
|
17
|
+
|
18
|
+
USAGE
|
19
|
+
-----
|
20
|
+
|
21
|
+
Instantiate a client:
|
22
|
+
|
23
|
+
flockdb = Flock.new 'localhost:7915, :graphs => { :follows => 1, :blocks => 2 }
|
24
|
+
|
25
|
+
Flock.new is a convenience shortcut for Flock::Client.new. Flock.new
|
26
|
+
expects a list (or a single) servers as the first argument, and an
|
27
|
+
options hash. Flock::Client.new takes the same options as ThriftClient
|
28
|
+
for configuring the raw thrift client, as well as :graphs for mapping
|
29
|
+
ruby symbols to the corresponding graph ids. See [thrift_client's
|
30
|
+
documentation](http://github.com/fauna/thrift_client) for information
|
31
|
+
on the other options it takes.
|
32
|
+
|
33
|
+
Edge manipulation:
|
34
|
+
|
35
|
+
flockdb.add(1, :follows, 2)
|
36
|
+
flockdb.remove(1, :blocks, 2)
|
37
|
+
|
38
|
+
Perform a query:
|
39
|
+
|
40
|
+
flockdb.select(1, :follows, nil).to_a
|
41
|
+
|
42
|
+
The client supports a rich query api to build up complex set operations:
|
43
|
+
|
44
|
+
flockdb.select(nil, :follows, 1).difference(flockdb.select(1, :follows, nil).intersect(2, :follows, nil)).to_a
|
45
|
+
|
46
|
+
|
47
|
+
CONTRIBUTORS
|
48
|
+
------------
|
49
|
+
|
50
|
+
Nick Kallen
|
51
|
+
Matt Freels
|
52
|
+
Rael Dornfest
|
53
|
+
|
54
|
+
|
55
|
+
LICENSE
|
56
|
+
-------
|
57
|
+
|
58
|
+
Copyright (C) 2010 Twitter, Inc.
|
59
|
+
|
60
|
+
This work is licensed under the Apache License, Version 2.0. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
ROOT_DIR = File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'rubygems' rescue nil
|
4
|
+
require 'rake'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc "Run all specs in spec directory."
|
10
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
11
|
+
t.spec_opts = ['--options', "\"#{ROOT_DIR}/spec/spec.opts\""]
|
12
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
# gemification with jeweler
|
16
|
+
begin
|
17
|
+
require 'jeweler'
|
18
|
+
Jeweler::Tasks.new do |gemspec|
|
19
|
+
gemspec.name = "flockdb"
|
20
|
+
gemspec.summary = "Ruby Flock client"
|
21
|
+
gemspec.description = "Get your flock on in Ruby."
|
22
|
+
gemspec.email = "freels@twitter.com"
|
23
|
+
gemspec.homepage = "http://twitter.com"
|
24
|
+
gemspec.authors = ["Matt Freels", "Rael Dornfest", "Nick Kallen"]
|
25
|
+
gemspec.add_dependency 'thrift', '>= 0.2.0'
|
26
|
+
gemspec.add_dependency 'thrift_client', '>= 0.4.1'
|
27
|
+
|
28
|
+
# development
|
29
|
+
gemspec.add_development_dependency 'rspec'
|
30
|
+
gemspec.add_development_dependency 'rr'
|
31
|
+
end
|
32
|
+
rescue LoadError
|
33
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
34
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
data/flockdb.gemspec
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{flockdb}
|
8
|
+
s.version = "0.3.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Matt Freels", "Rael Dornfest", "Nick Kallen"]
|
12
|
+
s.date = %q{2010-04-19}
|
13
|
+
s.description = %q{Get your flock on in Ruby.}
|
14
|
+
s.email = %q{freels@twitter.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"flockdb.gemspec",
|
26
|
+
"lib/flock.rb",
|
27
|
+
"lib/flock/client.rb",
|
28
|
+
"lib/flock/mixins/sizeable.rb",
|
29
|
+
"lib/flock/mock_service.rb",
|
30
|
+
"lib/flock/operation.rb",
|
31
|
+
"lib/flock/operations/complex_operation.rb",
|
32
|
+
"lib/flock/operations/execute_operation.rb",
|
33
|
+
"lib/flock/operations/execute_operations.rb",
|
34
|
+
"lib/flock/operations/query_term.rb",
|
35
|
+
"lib/flock/operations/select_operation.rb",
|
36
|
+
"lib/flock/operations/simple_operation.rb",
|
37
|
+
"lib/flock/service.rb",
|
38
|
+
"lib/flock/thrift/edges.rb",
|
39
|
+
"lib/flock/thrift/edges_types.rb",
|
40
|
+
"lib/flock/thrift/flock_constants.rb",
|
41
|
+
"lib/flock/thrift/flock_types.rb",
|
42
|
+
"lib/flock/thrift/shards.rb",
|
43
|
+
"lib/flockdb.rb",
|
44
|
+
"spec/flock_spec.rb",
|
45
|
+
"spec/mock_service_spec.rb",
|
46
|
+
"spec/spec.opts",
|
47
|
+
"spec/spec_helper.rb"
|
48
|
+
]
|
49
|
+
s.homepage = %q{http://twitter.com}
|
50
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = %q{1.3.6}
|
53
|
+
s.summary = %q{Ruby Flock client}
|
54
|
+
s.test_files = [
|
55
|
+
"spec/flock_spec.rb",
|
56
|
+
"spec/mock_service_spec.rb",
|
57
|
+
"spec/spec_helper.rb"
|
58
|
+
]
|
59
|
+
|
60
|
+
if s.respond_to? :specification_version then
|
61
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
62
|
+
s.specification_version = 3
|
63
|
+
|
64
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
65
|
+
s.add_runtime_dependency(%q<thrift>, [">= 0.2.0"])
|
66
|
+
s.add_runtime_dependency(%q<thrift_client>, [">= 0.4.1"])
|
67
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<thrift>, [">= 0.2.0"])
|
71
|
+
s.add_dependency(%q<thrift_client>, [">= 0.4.1"])
|
72
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
73
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<thrift>, [">= 0.2.0"])
|
77
|
+
s.add_dependency(%q<thrift_client>, [">= 0.4.1"])
|
78
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
79
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
data/lib/flock.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'thrift'
|
3
|
+
require 'thrift_client'
|
4
|
+
|
5
|
+
require 'flock/mixins/sizeable'
|
6
|
+
|
7
|
+
# thrift sources. load order is important.
|
8
|
+
require 'flock/thrift/flock_types'
|
9
|
+
require 'flock/thrift/flock_constants'
|
10
|
+
require 'flock/thrift/shards'
|
11
|
+
require 'flock/thrift/edges_types'
|
12
|
+
require 'flock/thrift/edges'
|
13
|
+
|
14
|
+
require 'flock/operation'
|
15
|
+
require 'flock/operations/query_term'
|
16
|
+
require 'flock/operations/select_operation'
|
17
|
+
require 'flock/operations/complex_operation'
|
18
|
+
require 'flock/operations/simple_operation'
|
19
|
+
require 'flock/operations/execute_operation'
|
20
|
+
require 'flock/operations/execute_operations'
|
21
|
+
require 'flock/service'
|
22
|
+
require 'flock/client'
|
23
|
+
|
24
|
+
module Flock
|
25
|
+
autoload :MockService, 'flock/mock_service'
|
26
|
+
|
27
|
+
class UnknownGraphError < FlockException
|
28
|
+
def initialize(graph)
|
29
|
+
super("Unable to look up id for graph #{graph.inspect}. Register graphs with Flock.graphs = <graph list>.")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
attr_accessor :default_service_class, :graphs
|
35
|
+
|
36
|
+
def new(*args)
|
37
|
+
Flock::Client.new(*args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/flock/client.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
class Flock::Client
|
2
|
+
|
3
|
+
attr_accessor :graphs
|
4
|
+
attr_reader :service
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def service_class; Flock.default_service_class ||= Flock::Service end
|
8
|
+
end
|
9
|
+
|
10
|
+
# takes arguments a list of servers and an options hash to pass to the default service_class,
|
11
|
+
# or a service itself
|
12
|
+
def initialize(servers = nil, options = {})
|
13
|
+
if graphs = (options.delete(:graphs) || Flock.graphs)
|
14
|
+
@graphs = graphs
|
15
|
+
end
|
16
|
+
|
17
|
+
@service =
|
18
|
+
if servers.nil? or servers.is_a? Array or servers.is_a? String
|
19
|
+
self.class.service_class.new(servers, options)
|
20
|
+
else
|
21
|
+
servers
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# local results cache
|
27
|
+
|
28
|
+
def cache_locally
|
29
|
+
@cache = {}
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
@cache = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def _cache(op, query)
|
36
|
+
return yield unless @cache
|
37
|
+
@cache[[op, query]] ||= yield
|
38
|
+
end
|
39
|
+
|
40
|
+
def _cache_clear
|
41
|
+
@cache = {} if @cache
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# queries
|
46
|
+
|
47
|
+
def select(*query)
|
48
|
+
Flock::SimpleOperation.new(self, _query_args(query))
|
49
|
+
end
|
50
|
+
|
51
|
+
def contains(*query)
|
52
|
+
_cache :contains, query do
|
53
|
+
select(*query).any?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def size(*query)
|
58
|
+
_cache :size, query do
|
59
|
+
select(*query).size
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias count size
|
63
|
+
|
64
|
+
|
65
|
+
# edge manipulation
|
66
|
+
|
67
|
+
def update(method, source_id, graph, destination_id, priority = Flock::Priority::High)
|
68
|
+
_cache_clear
|
69
|
+
ops = current_transaction || Flock::ExecuteOperations.new(@service, priority)
|
70
|
+
ops.send(method, *_query_args([source_id, graph, destination_id]))
|
71
|
+
ops.apply unless in_transaction?
|
72
|
+
end
|
73
|
+
|
74
|
+
[:add, :remove, :archive, :unarchive].each do |method|
|
75
|
+
class_eval "def #{method}(*args); update(#{method.inspect}, *args) end", __FILE__, __LINE__
|
76
|
+
end
|
77
|
+
|
78
|
+
def transaction(priority = Flock::Priority::High, &block)
|
79
|
+
new_transaction = !in_transaction?
|
80
|
+
|
81
|
+
ops =
|
82
|
+
if new_transaction
|
83
|
+
Thread.current[:edge_transaction] = Flock::ExecuteOperations.new(@service, priority)
|
84
|
+
else
|
85
|
+
current_transaction
|
86
|
+
end
|
87
|
+
|
88
|
+
result = yield self if block.arity == 1
|
89
|
+
ops.apply if new_transaction
|
90
|
+
result
|
91
|
+
|
92
|
+
ensure
|
93
|
+
Thread.current[:edge_transaction] = nil if new_transaction
|
94
|
+
end
|
95
|
+
|
96
|
+
def current_transaction
|
97
|
+
Thread.current[:edge_transaction]
|
98
|
+
end
|
99
|
+
|
100
|
+
def in_transaction?
|
101
|
+
!!Thread.current[:edge_transaction]
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# graph name lookup utility methods
|
106
|
+
|
107
|
+
def _lookup_graph(key)
|
108
|
+
if @graphs.nil? or key.is_a? Integer
|
109
|
+
key
|
110
|
+
else
|
111
|
+
@graphs[key] or raise UnknownGraphError.new(key)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def _query_args(args)
|
116
|
+
source, graph, destination = *((args.length == 1) ? args.first : args)
|
117
|
+
[_node_arg(source), _lookup_graph(graph), _node_arg(destination)]
|
118
|
+
end
|
119
|
+
|
120
|
+
def _node_arg(node)
|
121
|
+
return node.map {|n| n.to_i if n } if node.respond_to? :map
|
122
|
+
node.to_i if node
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Flock
|
2
|
+
module MockService
|
3
|
+
extend self
|
4
|
+
|
5
|
+
attr_accessor :timeout, :fixtures
|
6
|
+
|
7
|
+
def clear
|
8
|
+
@sources = @destinations = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def load(fixtures = nil)
|
12
|
+
fixtures ||= self.fixtures or raise "No flock fixtures specified. either pass fixtures to load, or set Flock::MockService.fixtures."
|
13
|
+
|
14
|
+
clear
|
15
|
+
|
16
|
+
fixtures.each do |fixture|
|
17
|
+
file, graph_id, source, destination = fixture.values_at(:file, :graph, :source, :destination)
|
18
|
+
|
19
|
+
fixtures_data = YAML::load(ERB.new(File.open(file, 'r').read).result(binding)).sort
|
20
|
+
fixtures_data.each do |key, value|
|
21
|
+
sources[[value[destination], graph_id]] << value[source]
|
22
|
+
destinations[[value[source], graph_id]] << value[destination]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"Flock::MockService: " + @sources.inspect + " - " + @destinations.inspect
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute(operations)
|
32
|
+
operations = operations.operations
|
33
|
+
operations.each do |operation|
|
34
|
+
term = operation.term
|
35
|
+
backward_data_source, forward_data_source = term.is_forward ? [sources, destinations] : [destinations, sources]
|
36
|
+
backward_archived_data_source, forward_archived_data_source = term.is_forward ? [archived_sources, archived_destinations] : [archived_destinations, archived_sources]
|
37
|
+
source_id, graph_id = term.source_id, term.graph_id
|
38
|
+
destination_ids = term.destination_ids && term.destination_ids.unpack("Q*")
|
39
|
+
case operation.operation_type
|
40
|
+
when Edges::ExecuteOperationType::Add
|
41
|
+
if destination_ids.nil?
|
42
|
+
backward_archived_data_source.delete([source_id, graph_id]).to_a.each do |n|
|
43
|
+
(forward_data_source[[n, graph_id]] << source_id).uniq!
|
44
|
+
(backward_data_source[[source_id, graph_id]] << n).uniq!
|
45
|
+
forward_archived_data_source[[n, graph_id]].delete(source_id)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
destination_ids.each do |destination_id|
|
49
|
+
backward_data_source[[destination_id, graph_id]] << source_id
|
50
|
+
forward_data_source[[source_id, graph_id]] << destination_id
|
51
|
+
end
|
52
|
+
end
|
53
|
+
when Edges::ExecuteOperationType::Remove
|
54
|
+
destination_ids.each do |destination_id|
|
55
|
+
backward_data_source[[destination_id, graph_id]].delete(source_id)
|
56
|
+
forward_data_source[[source_id, graph_id]].delete(destination_id)
|
57
|
+
end
|
58
|
+
when Edges::ExecuteOperationType::Archive
|
59
|
+
if destination_ids.nil?
|
60
|
+
backward_data_source.delete([source_id, graph_id]).to_a.each do |n|
|
61
|
+
forward_archived_data_source[[n, graph_id]] << source_id
|
62
|
+
backward_archived_data_source[[source_id, graph_id]] << n
|
63
|
+
forward_data_source[[n, graph_id]].delete(source_id)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise "not yet implemented"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def select(select_operations, page)
|
73
|
+
iterate(select_query(select_operations), page)
|
74
|
+
end
|
75
|
+
|
76
|
+
def count(select_operations)
|
77
|
+
select_query(select_operations).size
|
78
|
+
end
|
79
|
+
|
80
|
+
# FIXME
|
81
|
+
def counts_of_sources_for(destination_ids, graph_id)
|
82
|
+
destination_ids.unpack("Q*").map do |destination_id|
|
83
|
+
(sources[[destination_id, graph_id]] || []).size
|
84
|
+
end.pack("I*")
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def select_query(select_operations)
|
89
|
+
stack = []
|
90
|
+
select_operations.each do |select_operation|
|
91
|
+
case select_operation.operation_type
|
92
|
+
when Edges::SelectOperationType::SimpleQuery
|
93
|
+
query_term = select_operation.term
|
94
|
+
data_source = query_term.is_forward ? destinations : sources
|
95
|
+
data = data_source[[query_term.source_id, query_term.graph_id]]
|
96
|
+
if query_term.destination_ids
|
97
|
+
data &= query_term.destination_ids.unpack("Q*")
|
98
|
+
end
|
99
|
+
stack.push(data)
|
100
|
+
when Edges::SelectOperationType::Intersection
|
101
|
+
stack.push(stack.pop & stack.pop)
|
102
|
+
when Edges::SelectOperationType::Union
|
103
|
+
stack.push(stack.pop | stack.pop)
|
104
|
+
when Edges::SelectOperationType::Difference
|
105
|
+
operand2 = stack.pop
|
106
|
+
operand1 = stack.pop
|
107
|
+
stack.push(operand1 - operand2)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
stack.pop
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def iterate(data, page)
|
116
|
+
return empty_result if page.cursor == Flock::CursorEnd
|
117
|
+
|
118
|
+
start = if page.cursor < Flock::CursorStart
|
119
|
+
[-page.cursor - page.count, 0].max
|
120
|
+
else
|
121
|
+
page.cursor == Flock::CursorStart ? 0 : page.cursor
|
122
|
+
end
|
123
|
+
rv = data.slice(start, page.count)
|
124
|
+
next_cursor = (start + page.count >= data.size) ? Flock::CursorEnd : start + page.count
|
125
|
+
prev_cursor = (start <= 0) ? Flock::CursorEnd : -start
|
126
|
+
|
127
|
+
result = Flock::Results.new
|
128
|
+
result.ids = Array(rv).pack("Q*")
|
129
|
+
result.next_cursor = next_cursor
|
130
|
+
result.prev_cursor = prev_cursor
|
131
|
+
result
|
132
|
+
end
|
133
|
+
|
134
|
+
def empty_result
|
135
|
+
@empty_result ||= begin
|
136
|
+
empty_result = Flock::Results.new
|
137
|
+
empty_result.ids = ""
|
138
|
+
empty_result.next_cursor = Flock::CursorEnd
|
139
|
+
empty_result.prev_cursor = Flock::CursorEnd
|
140
|
+
empty_result
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def sources
|
145
|
+
@sources ||= Hash.new do |h, k|
|
146
|
+
h[k] = []
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def destinations
|
151
|
+
@destinations ||= Hash.new do |h, k|
|
152
|
+
h[k] = []
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def archived_sources
|
157
|
+
@archived_sources ||= Hash.new do |h, k|
|
158
|
+
h[k] = []
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def archived_destinations
|
163
|
+
@archived_destinations ||= Hash.new do |h, k|
|
164
|
+
h[k] = []
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# deprecated
|
169
|
+
public
|
170
|
+
def offset_sources_for(destination_id, graph_id, offset, count)
|
171
|
+
(sources[[destination_id, graph_id]].slice(offset, count) || []).pack("Q*")
|
172
|
+
end
|
173
|
+
|
174
|
+
def offset_destinations_for(source_id, graph_id, offset, count)
|
175
|
+
(destinations[[source_id, graph_id]].slice(offset, count) || []).pack("Q*")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|