seomoz-riak-client 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +27 -0
- data/Guardfile +14 -0
- data/Rakefile +76 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +174 -0
- data/erl_src/riak_search_test_backend.beam +0 -0
- data/erl_src/riak_search_test_backend.erl +175 -0
- data/lib/active_support/cache/riak_store.rb +2 -0
- data/lib/riak.rb +21 -0
- data/lib/riak/bucket.rb +215 -0
- data/lib/riak/cache_store.rb +84 -0
- data/lib/riak/client.rb +415 -0
- data/lib/riak/client/beefcake/messages.rb +147 -0
- data/lib/riak/client/beefcake/object_methods.rb +92 -0
- data/lib/riak/client/beefcake_protobuffs_backend.rb +176 -0
- data/lib/riak/client/excon_backend.rb +65 -0
- data/lib/riak/client/http_backend.rb +203 -0
- data/lib/riak/client/http_backend/configuration.rb +46 -0
- data/lib/riak/client/http_backend/key_streamer.rb +43 -0
- data/lib/riak/client/http_backend/object_methods.rb +94 -0
- data/lib/riak/client/http_backend/request_headers.rb +34 -0
- data/lib/riak/client/http_backend/transport_methods.rb +218 -0
- data/lib/riak/client/net_http_backend.rb +79 -0
- data/lib/riak/client/protobuffs_backend.rb +97 -0
- data/lib/riak/client/pump.rb +30 -0
- data/lib/riak/client/search.rb +94 -0
- data/lib/riak/core_ext.rb +6 -0
- data/lib/riak/core_ext/blank.rb +53 -0
- data/lib/riak/core_ext/extract_options.rb +7 -0
- data/lib/riak/core_ext/json.rb +15 -0
- data/lib/riak/core_ext/slice.rb +18 -0
- data/lib/riak/core_ext/stringify_keys.rb +10 -0
- data/lib/riak/core_ext/symbolize_keys.rb +10 -0
- data/lib/riak/core_ext/to_param.rb +31 -0
- data/lib/riak/encoding.rb +6 -0
- data/lib/riak/failed_request.rb +81 -0
- data/lib/riak/i18n.rb +3 -0
- data/lib/riak/json.rb +28 -0
- data/lib/riak/link.rb +85 -0
- data/lib/riak/locale/en.yml +48 -0
- data/lib/riak/map_reduce.rb +206 -0
- data/lib/riak/map_reduce/filter_builder.rb +94 -0
- data/lib/riak/map_reduce/phase.rb +98 -0
- data/lib/riak/map_reduce_error.rb +7 -0
- data/lib/riak/robject.rb +290 -0
- data/lib/riak/search.rb +3 -0
- data/lib/riak/serializers.rb +74 -0
- data/lib/riak/stamp.rb +77 -0
- data/lib/riak/test_server.rb +252 -0
- data/lib/riak/util/escape.rb +45 -0
- data/lib/riak/util/fiber1.8.rb +48 -0
- data/lib/riak/util/headers.rb +53 -0
- data/lib/riak/util/multipart.rb +52 -0
- data/lib/riak/util/multipart/stream_parser.rb +62 -0
- data/lib/riak/util/tcp_socket_extensions.rb +58 -0
- data/lib/riak/util/translation.rb +19 -0
- data/lib/riak/walk_spec.rb +105 -0
- data/riak-client.gemspec +55 -0
- data/seomoz-riak-client.gemspec +55 -0
- data/spec/fixtures/cat.jpg +0 -0
- data/spec/fixtures/multipart-blank.txt +7 -0
- data/spec/fixtures/multipart-mapreduce.txt +10 -0
- data/spec/fixtures/multipart-with-body.txt +16 -0
- data/spec/fixtures/server.cert.crt +15 -0
- data/spec/fixtures/server.cert.key +15 -0
- data/spec/fixtures/test.pem +1 -0
- data/spec/integration/riak/cache_store_spec.rb +154 -0
- data/spec/integration/riak/http_backends_spec.rb +58 -0
- data/spec/integration/riak/protobuffs_backends_spec.rb +32 -0
- data/spec/integration/riak/test_server_spec.rb +161 -0
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +59 -0
- data/spec/riak/bucket_spec.rb +205 -0
- data/spec/riak/client_spec.rb +517 -0
- data/spec/riak/core_ext/to_param_spec.rb +15 -0
- data/spec/riak/escape_spec.rb +69 -0
- data/spec/riak/excon_backend_spec.rb +64 -0
- data/spec/riak/headers_spec.rb +38 -0
- data/spec/riak/http_backend/configuration_spec.rb +51 -0
- data/spec/riak/http_backend/object_methods_spec.rb +217 -0
- data/spec/riak/http_backend/transport_methods_spec.rb +117 -0
- data/spec/riak/http_backend_spec.rb +269 -0
- data/spec/riak/link_spec.rb +71 -0
- data/spec/riak/map_reduce/filter_builder_spec.rb +32 -0
- data/spec/riak/map_reduce/phase_spec.rb +136 -0
- data/spec/riak/map_reduce_spec.rb +310 -0
- data/spec/riak/multipart_spec.rb +23 -0
- data/spec/riak/net_http_backend_spec.rb +16 -0
- data/spec/riak/robject_spec.rb +427 -0
- data/spec/riak/search_spec.rb +178 -0
- data/spec/riak/serializers_spec.rb +93 -0
- data/spec/riak/stamp_spec.rb +54 -0
- data/spec/riak/stream_parser_spec.rb +53 -0
- data/spec/riak/walk_spec_spec.rb +195 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/drb_mock_server.rb +39 -0
- data/spec/support/http_backend_implementation_examples.rb +266 -0
- data/spec/support/integration_setup.rb +10 -0
- data/spec/support/mock_server.rb +81 -0
- data/spec/support/mocks.rb +4 -0
- data/spec/support/test_server.yml.example +2 -0
- data/spec/support/unified_backend_examples.rb +255 -0
- metadata +271 -0
data/Gemfile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
gem 'bundler'
|
5
|
+
gem 'guard-rspec'
|
6
|
+
gem 'rb-fsevent'
|
7
|
+
gem 'growl'
|
8
|
+
|
9
|
+
platforms :mri do
|
10
|
+
gem 'yajl-ruby'
|
11
|
+
end
|
12
|
+
|
13
|
+
platforms :jruby do
|
14
|
+
gem 'jruby-openssl'
|
15
|
+
end
|
16
|
+
|
17
|
+
group :integration do
|
18
|
+
gem 'activesupport', '~>3.0'
|
19
|
+
end
|
20
|
+
|
21
|
+
platforms :mri_18, :jruby do
|
22
|
+
gem 'ruby-debug'
|
23
|
+
end
|
24
|
+
|
25
|
+
platforms :mri_19 do
|
26
|
+
gem 'ruby-debug19'
|
27
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
gemset = ENV['RVM_GEMSET'] || 'ripple'
|
4
|
+
gemset = "@#{gemset}" unless gemset.to_s == ''
|
5
|
+
|
6
|
+
rvms = %w[ 1.8.7 1.9.2 jruby ].map do |version|
|
7
|
+
"#{version}@#{gemset}"
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'rspec', :cli => '--tag ~integration', :rvm => rvms do
|
11
|
+
watch(%r{^spec/.+_spec\.rb$})
|
12
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
13
|
+
watch('spec/spec_helper.rb') { "spec/riak" }
|
14
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
gemspec = Gem::Specification.new do |gem|
|
5
|
+
gem.name = "seomoz-riak-client"
|
6
|
+
gem.summary = %Q{riak-client is a rich client for Riak, the distributed database by Basho.}
|
7
|
+
gem.description = %Q{riak-client is a rich client for Riak, the distributed database by Basho. It supports the full HTTP interface including storage operations, bucket configuration, link-walking and map-reduce.}
|
8
|
+
gem.version = File.read('../VERSION').strip
|
9
|
+
gem.email = "sean@basho.com"
|
10
|
+
gem.homepage = "http://seancribbs.github.com/ripple"
|
11
|
+
gem.authors = ["Sean Cribbs"]
|
12
|
+
gem.add_development_dependency "rspec", "~>2.6.0"
|
13
|
+
gem.add_development_dependency "fakeweb", ">=1.2"
|
14
|
+
gem.add_development_dependency "rack", ">=1.0"
|
15
|
+
gem.add_development_dependency "excon", "~>0.6.1"
|
16
|
+
gem.add_development_dependency 'rake', '~> 0.8.7'
|
17
|
+
gem.add_dependency "i18n", ">=0.4.0"
|
18
|
+
gem.add_dependency "builder", ">= 2.1.2"
|
19
|
+
gem.add_dependency "beefcake", "0.3.2"
|
20
|
+
gem.add_dependency "multi_json", "~>1.0.0"
|
21
|
+
|
22
|
+
files = FileList["**/*"]
|
23
|
+
# Editor and O/S files
|
24
|
+
files.exclude ".DS_Store", "*~", "\#*", ".\#*", "*.swp", "*.tmproj", "tmtags"
|
25
|
+
# Generated artifacts
|
26
|
+
files.exclude "coverage", "rdoc", "pkg", "doc", ".bundle", "*.rbc", ".rvmrc", ".watchr", ".rspec"
|
27
|
+
# Project-specific
|
28
|
+
files.exclude "Gemfile.lock", %r{spec/support/test_server.yml$}, "bin"
|
29
|
+
# Remove directories
|
30
|
+
files.exclude {|d| File.directory?(d) }
|
31
|
+
|
32
|
+
gem.files = files.to_a
|
33
|
+
|
34
|
+
gem.test_files = gem.files.grep(/_spec\.rb$/)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Gem packaging tasks
|
38
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
39
|
+
pkg.need_zip = false
|
40
|
+
pkg.need_tar = false
|
41
|
+
end
|
42
|
+
|
43
|
+
task :gem => :gemspec
|
44
|
+
|
45
|
+
desc %{Build the gemspec file.}
|
46
|
+
task :gemspec do
|
47
|
+
gemspec.validate
|
48
|
+
File.open("#{gemspec.name}.gemspec", 'w'){|f| f.write gemspec.to_ruby }
|
49
|
+
end
|
50
|
+
|
51
|
+
desc %{Release the gem to RubyGems.org}
|
52
|
+
task :release => :gem do
|
53
|
+
system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
54
|
+
end
|
55
|
+
|
56
|
+
require 'rspec/core'
|
57
|
+
require 'rspec/core/rake_task'
|
58
|
+
|
59
|
+
desc "Run Unit Specs Only"
|
60
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
61
|
+
spec.pattern = "spec/riak/**/*_spec.rb"
|
62
|
+
spec.rspec_opts = %w[--profile]
|
63
|
+
end
|
64
|
+
|
65
|
+
namespace :spec do
|
66
|
+
desc "Run Integration Specs Only"
|
67
|
+
RSpec::Core::RakeTask.new(:integration) do |spec|
|
68
|
+
spec.pattern = "spec/integration/**/*_spec.rb"
|
69
|
+
spec.rspec_opts = %w[--profile]
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Run All Specs"
|
73
|
+
task :all => %w[ spec spec:integration ]
|
74
|
+
end
|
75
|
+
|
76
|
+
task :default => "spec:all"
|
Binary file
|
@@ -0,0 +1,174 @@
|
|
1
|
+
%% -------------------------------------------------------------------
|
2
|
+
%%
|
3
|
+
%% riak_kv_test_backend: storage engine based on ETS tables
|
4
|
+
%%
|
5
|
+
%%
|
6
|
+
%% -------------------------------------------------------------------
|
7
|
+
|
8
|
+
% @doc riak_kv_test_backend is a Riak storage backend using ets that
|
9
|
+
% exposes a reset function for efficiently clearing stored data.
|
10
|
+
|
11
|
+
-module(riak_kv_test_backend).
|
12
|
+
-behavior(riak_kv_backend).
|
13
|
+
-behavior(gen_server).
|
14
|
+
-ifdef(TEST).
|
15
|
+
-include_lib("eunit/include/eunit.hrl").
|
16
|
+
-endif.
|
17
|
+
-export([start/2,stop/1,get/2,put/3,list/1,list_bucket/2,delete/2,
|
18
|
+
is_empty/1, drop/1, fold/3, callback/3, reset/0]).
|
19
|
+
|
20
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
21
|
+
terminate/2, code_change/3]).
|
22
|
+
|
23
|
+
|
24
|
+
% @type state() = term().
|
25
|
+
-record(state, {t, p}).
|
26
|
+
|
27
|
+
% @spec start(Partition :: integer(), Config :: proplist()) ->
|
28
|
+
% {ok, state()} | {{error, Reason :: term()}, state()}
|
29
|
+
start(Partition, _Config) ->
|
30
|
+
gen_server:start_link(?MODULE, [Partition], []).
|
31
|
+
|
32
|
+
% @spec reset() -> ok | {error, timeout}
|
33
|
+
reset() ->
|
34
|
+
Pids = lists:foldl(fun(Item, Acc) ->
|
35
|
+
case lists:prefix("test_backend", atom_to_list(Item)) of
|
36
|
+
true -> [whereis(Item)|Acc];
|
37
|
+
_ -> Acc
|
38
|
+
end
|
39
|
+
end, [], registered()),
|
40
|
+
[gen_server:cast(Pid,{reset, self()})|| Pid <- Pids],
|
41
|
+
receive_reset(Pids).
|
42
|
+
|
43
|
+
receive_reset([]) -> ok;
|
44
|
+
receive_reset(Pids) ->
|
45
|
+
receive
|
46
|
+
{reset, Pid} ->
|
47
|
+
receive_reset(lists:delete(Pid, Pids))
|
48
|
+
after 1000 ->
|
49
|
+
{error, timeout}
|
50
|
+
end.
|
51
|
+
|
52
|
+
%% @private
|
53
|
+
init([Partition]) ->
|
54
|
+
PName = list_to_atom("test_backend" ++ integer_to_list(Partition)),
|
55
|
+
P = list_to_atom(integer_to_list(Partition)),
|
56
|
+
register(PName, self()),
|
57
|
+
{ok, #state{t=ets:new(P,[]), p=P}}.
|
58
|
+
|
59
|
+
%% @private
|
60
|
+
handle_cast({reset,From}, State) ->
|
61
|
+
ets:delete_all_objects(State#state.t),
|
62
|
+
From ! {reset, self()},
|
63
|
+
{noreply, State};
|
64
|
+
handle_cast(_, State) -> {noreply, State}.
|
65
|
+
|
66
|
+
%% @private
|
67
|
+
handle_call(stop,_From,State) -> {reply, srv_stop(State), State};
|
68
|
+
handle_call({get,BKey},_From,State) -> {reply, srv_get(State,BKey), State};
|
69
|
+
handle_call({put,BKey,Val},_From,State) ->
|
70
|
+
{reply, srv_put(State,BKey,Val),State};
|
71
|
+
handle_call({delete,BKey},_From,State) -> {reply, srv_delete(State,BKey),State};
|
72
|
+
handle_call(list,_From,State) -> {reply, srv_list(State), State};
|
73
|
+
handle_call({list_bucket,Bucket},_From,State) ->
|
74
|
+
{reply, srv_list_bucket(State, Bucket), State};
|
75
|
+
handle_call(is_empty, _From, State) ->
|
76
|
+
{reply, ets:info(State#state.t, size) =:= 0, State};
|
77
|
+
handle_call(drop, _From, State) ->
|
78
|
+
ets:delete(State#state.t),
|
79
|
+
{reply, ok, State};
|
80
|
+
handle_call({fold, Fun0, Acc}, _From, State) ->
|
81
|
+
Fun = fun({{B,K}, V}, AccIn) -> Fun0({B,K}, V, AccIn) end,
|
82
|
+
Reply = ets:foldl(Fun, Acc, State#state.t),
|
83
|
+
{reply, Reply, State}.
|
84
|
+
|
85
|
+
% @spec stop(state()) -> ok | {error, Reason :: term()}
|
86
|
+
stop(SrvRef) -> gen_server:call(SrvRef,stop).
|
87
|
+
srv_stop(State) ->
|
88
|
+
true = ets:delete(State#state.t),
|
89
|
+
ok.
|
90
|
+
|
91
|
+
% get(state(), riak_object:bkey()) ->
|
92
|
+
% {ok, Val :: binary()} | {error, Reason :: term()}
|
93
|
+
% key must be 160b
|
94
|
+
get(SrvRef, BKey) -> gen_server:call(SrvRef,{get,BKey}).
|
95
|
+
srv_get(State, BKey) ->
|
96
|
+
case ets:lookup(State#state.t,BKey) of
|
97
|
+
[] -> {error, notfound};
|
98
|
+
[{BKey,Val}] -> {ok, Val};
|
99
|
+
Err -> {error, Err}
|
100
|
+
end.
|
101
|
+
|
102
|
+
% put(state(), riak_object:bkey(), Val :: binary()) ->
|
103
|
+
% ok | {error, Reason :: term()}
|
104
|
+
% key must be 160b
|
105
|
+
put(SrvRef, BKey, Val) -> gen_server:call(SrvRef,{put,BKey,Val}).
|
106
|
+
srv_put(State,BKey,Val) ->
|
107
|
+
true = ets:insert(State#state.t, {BKey,Val}),
|
108
|
+
ok.
|
109
|
+
|
110
|
+
% delete(state(), riak_object:bkey()) ->
|
111
|
+
% ok | {error, Reason :: term()}
|
112
|
+
% key must be 160b
|
113
|
+
delete(SrvRef, BKey) -> gen_server:call(SrvRef,{delete,BKey}).
|
114
|
+
srv_delete(State, BKey) ->
|
115
|
+
true = ets:delete(State#state.t, BKey),
|
116
|
+
ok.
|
117
|
+
|
118
|
+
% list(state()) -> [riak_object:bkey()]
|
119
|
+
list(SrvRef) -> gen_server:call(SrvRef,list).
|
120
|
+
srv_list(State) ->
|
121
|
+
MList = ets:match(State#state.t,{'$1','_'}),
|
122
|
+
list(MList,[]).
|
123
|
+
list([],Acc) -> Acc;
|
124
|
+
list([[K]|Rest],Acc) -> list(Rest,[K|Acc]).
|
125
|
+
|
126
|
+
% list_bucket(term(), Bucket :: riak_object:bucket()) -> [Key :: binary()]
|
127
|
+
list_bucket(SrvRef, Bucket) ->
|
128
|
+
gen_server:call(SrvRef,{list_bucket, Bucket}).
|
129
|
+
srv_list_bucket(State, {filter, Bucket, Fun}) ->
|
130
|
+
MList = lists:filter(Fun, ets:match(State#state.t,{{Bucket,'$1'},'_'})),
|
131
|
+
list(MList,[]);
|
132
|
+
srv_list_bucket(State, Bucket) ->
|
133
|
+
case Bucket of
|
134
|
+
'_' -> MatchSpec = {{'$1','_'},'_'};
|
135
|
+
_ -> MatchSpec = {{Bucket,'$1'},'_'}
|
136
|
+
end,
|
137
|
+
MList = ets:match(State#state.t,MatchSpec),
|
138
|
+
list(MList,[]).
|
139
|
+
|
140
|
+
is_empty(SrvRef) -> gen_server:call(SrvRef, is_empty).
|
141
|
+
|
142
|
+
drop(SrvRef) -> gen_server:call(SrvRef, drop).
|
143
|
+
|
144
|
+
fold(SrvRef, Fun, Acc0) -> gen_server:call(SrvRef, {fold, Fun, Acc0}, infinity).
|
145
|
+
|
146
|
+
%% Ignore callbacks for other backends so multi backend works
|
147
|
+
callback(_State, _Ref, _Msg) ->
|
148
|
+
ok.
|
149
|
+
|
150
|
+
%% @private
|
151
|
+
handle_info(_Msg, State) -> {noreply, State}.
|
152
|
+
|
153
|
+
%% @private
|
154
|
+
terminate(_Reason, _State) -> ok.
|
155
|
+
|
156
|
+
%% @private
|
157
|
+
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
158
|
+
|
159
|
+
%%
|
160
|
+
%% Test
|
161
|
+
%%
|
162
|
+
-ifdef(TEST).
|
163
|
+
|
164
|
+
% @private
|
165
|
+
simple_test() ->
|
166
|
+
riak_kv_backend:standard_test(?MODULE, []).
|
167
|
+
|
168
|
+
-ifdef(EQC).
|
169
|
+
%% @private
|
170
|
+
eqc_test() ->
|
171
|
+
?assertEqual(true, backend_eqc:test(?MODULE, true)).
|
172
|
+
|
173
|
+
-endif. % EQC
|
174
|
+
-endif. % TEST
|
Binary file
|
@@ -0,0 +1,175 @@
|
|
1
|
+
%% -------------------------------------------------------------------
|
2
|
+
%%
|
3
|
+
%% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
|
4
|
+
%%
|
5
|
+
%% -------------------------------------------------------------------
|
6
|
+
|
7
|
+
-module(riak_search_test_backend).
|
8
|
+
-behavior(riak_search_backend).
|
9
|
+
|
10
|
+
-export([
|
11
|
+
reset/0,
|
12
|
+
start/2,
|
13
|
+
stop/1,
|
14
|
+
index/2,
|
15
|
+
delete/2,
|
16
|
+
stream/6,
|
17
|
+
range/8,
|
18
|
+
info/5,
|
19
|
+
fold/3,
|
20
|
+
is_empty/1,
|
21
|
+
drop/1
|
22
|
+
]).
|
23
|
+
-export([
|
24
|
+
stream_results/3
|
25
|
+
]).
|
26
|
+
|
27
|
+
-include("riak_search.hrl").
|
28
|
+
|
29
|
+
-record(state, {partition, table}).
|
30
|
+
|
31
|
+
reset() ->
|
32
|
+
{ok, Ring} = riak_core_ring_manager:get_my_ring(),
|
33
|
+
[ ets:delete_all_objects(list_to_atom(integer_to_list(P))) ||
|
34
|
+
P <- riak_core_ring:my_indices(Ring) ],
|
35
|
+
riak_search_config:clear(),
|
36
|
+
ok.
|
37
|
+
|
38
|
+
start(Partition, _Config) ->
|
39
|
+
Table = ets:new(list_to_atom(integer_to_list(Partition)),
|
40
|
+
[named_table, public, ordered_set]),
|
41
|
+
{ok, #state{partition=Partition, table=Table}}.
|
42
|
+
|
43
|
+
stop(State) ->
|
44
|
+
maybe_delete(State).
|
45
|
+
|
46
|
+
index(IFTVPKList, #state{table=Table}=State) ->
|
47
|
+
lists:foreach(
|
48
|
+
fun({I, F, T, V, P, K}) ->
|
49
|
+
Key = {b(I), b(F), b(T), b(V)},
|
50
|
+
case ets:lookup(Table, Key) of
|
51
|
+
[{_, _, ExistingKeyClock}] ->
|
52
|
+
if ExistingKeyClock > K ->
|
53
|
+
%% stored data is newer
|
54
|
+
ok;
|
55
|
+
true ->
|
56
|
+
%% stored data is older
|
57
|
+
ets:update_element(Table, Key,
|
58
|
+
[{2, P},{3, K}])
|
59
|
+
end;
|
60
|
+
[] ->
|
61
|
+
ets:insert(Table, {Key, P, K})
|
62
|
+
end
|
63
|
+
end,
|
64
|
+
IFTVPKList),
|
65
|
+
{reply, {indexed, node()}, State}.
|
66
|
+
|
67
|
+
delete(IFTVKList, State) ->
|
68
|
+
Table = State#state.table,
|
69
|
+
lists:foreach(fun(IFTVK) -> delete_fun(IFTVK, Table) end, IFTVKList),
|
70
|
+
{reply, {deleted, node()}, State}.
|
71
|
+
|
72
|
+
delete_fun({I, F, T, V, K}, Table) ->
|
73
|
+
Key = {b(I), b(F), b(T), b(V)},
|
74
|
+
case ets:lookup(Table, Key) of
|
75
|
+
[{Key, _Props, ExistingKeyClock}] ->
|
76
|
+
if ExistingKeyClock > K ->
|
77
|
+
%% stored data is newer
|
78
|
+
ok;
|
79
|
+
true ->
|
80
|
+
%% stored data is older
|
81
|
+
ets:delete(Table, Key)
|
82
|
+
end;
|
83
|
+
[] ->
|
84
|
+
ok
|
85
|
+
end;
|
86
|
+
delete_fun({I, F, T, V, _P, K}, Table) ->
|
87
|
+
%% copied idea from merge_index_backend
|
88
|
+
%% other operations include Props, though delete shouldn't
|
89
|
+
delete_fun({I, F, T, V, K}, Table).
|
90
|
+
|
91
|
+
info(Index, Field, Term, Sender, State) ->
|
92
|
+
Count = ets:select_count(State#state.table,
|
93
|
+
[{{{b(Index), b(Field), b(Term), '_'},
|
94
|
+
'_', '_'},
|
95
|
+
[],[true]}]),
|
96
|
+
riak_search_backend:info_response(Sender, [{Term, node(), Count}]),
|
97
|
+
noreply.
|
98
|
+
|
99
|
+
-define(STREAM_SIZE, 100).
|
100
|
+
|
101
|
+
range(Index, Field, StartTerm, EndTerm, _Size, FilterFun, Sender, State) ->
|
102
|
+
ST = b(StartTerm),
|
103
|
+
ET = b(EndTerm),
|
104
|
+
spawn(riak_search_ets_backend, stream_results,
|
105
|
+
[Sender,
|
106
|
+
FilterFun,
|
107
|
+
ets:select(State#state.table,
|
108
|
+
[{{{b(Index), b(Field), '$1', '$2'}, '$3', '_'},
|
109
|
+
[{'>=', '$1', ST}, {'=<', '$1', ET}],
|
110
|
+
[{{'$2', '$3'}}]}],
|
111
|
+
?STREAM_SIZE)]),
|
112
|
+
noreply.
|
113
|
+
|
114
|
+
stream(Index, Field, Term, FilterFun, Sender, State) ->
|
115
|
+
spawn(riak_search_ets_backend, stream_results,
|
116
|
+
[Sender,
|
117
|
+
FilterFun,
|
118
|
+
ets:select(State#state.table,
|
119
|
+
[{{{b(Index), b(Field), b(Term), '$1'}, '$2', '_'},
|
120
|
+
[], [{{'$1', '$2'}}]}],
|
121
|
+
?STREAM_SIZE)]),
|
122
|
+
noreply.
|
123
|
+
|
124
|
+
stream_results(Sender, FilterFun, {Results0, Continuation}) ->
|
125
|
+
case lists:filter(fun({V,P}) -> FilterFun(V, P) end, Results0) of
|
126
|
+
[] ->
|
127
|
+
ok;
|
128
|
+
Results ->
|
129
|
+
riak_search_backend:response_results(Sender, Results)
|
130
|
+
end,
|
131
|
+
stream_results(Sender, FilterFun, ets:select(Continuation));
|
132
|
+
stream_results(Sender, _, '$end_of_table') ->
|
133
|
+
riak_search_backend:response_done(Sender).
|
134
|
+
|
135
|
+
fold(FoldFun, Acc, State) ->
|
136
|
+
Fun = fun({{I,F,T,V},P,K}, {OuterAcc, {{I,{F,T}},InnerAcc}}) ->
|
137
|
+
%% same IFT, just accumulate doc/props/clock
|
138
|
+
{OuterAcc, {{I,{F,T}},[{V,P,K}|InnerAcc]}};
|
139
|
+
({{I,F,T,V},P,K}, {OuterAcc, {FoldKey, VPKList}}) ->
|
140
|
+
%% finished a string of IFT, send it off
|
141
|
+
%% (sorted order is assumed)
|
142
|
+
NewOuterAcc = FoldFun(FoldKey, VPKList, OuterAcc),
|
143
|
+
{NewOuterAcc, {{I,{F,T}},[{V,P,K}]}};
|
144
|
+
({{I,F,T,V},P,K}, {OuterAcc, undefined}) ->
|
145
|
+
%% first round through the fold - just start building
|
146
|
+
{OuterAcc, {{I,{F,T}},[{V,P,K}]}}
|
147
|
+
end,
|
148
|
+
{OuterAcc0, Final} = ets:foldl(Fun, {Acc, undefined}, State#state.table),
|
149
|
+
OuterAcc = case Final of
|
150
|
+
{FoldKey, VPKList} ->
|
151
|
+
%% one last IFT to send off
|
152
|
+
FoldFun(FoldKey, VPKList, OuterAcc0);
|
153
|
+
undefined ->
|
154
|
+
%% this partition was empty
|
155
|
+
OuterAcc0
|
156
|
+
end,
|
157
|
+
{reply, OuterAcc, State}.
|
158
|
+
|
159
|
+
is_empty(State) ->
|
160
|
+
0 == ets:info(State#state.table, size).
|
161
|
+
|
162
|
+
drop(State) ->
|
163
|
+
maybe_delete(State).
|
164
|
+
|
165
|
+
maybe_delete(State) ->
|
166
|
+
case lists:member(State#state.table, ets:all()) of
|
167
|
+
true ->
|
168
|
+
ets:delete(State#state.table),
|
169
|
+
ok;
|
170
|
+
false ->
|
171
|
+
ok
|
172
|
+
end.
|
173
|
+
|
174
|
+
b(Binary) when is_binary(Binary) -> Binary;
|
175
|
+
b(List) when is_list(List) -> iolist_to_binary(List).
|