seomoz-riak-client 1.0.0.pre

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.
Files changed (102) hide show
  1. data/Gemfile +27 -0
  2. data/Guardfile +14 -0
  3. data/Rakefile +76 -0
  4. data/erl_src/riak_kv_test_backend.beam +0 -0
  5. data/erl_src/riak_kv_test_backend.erl +174 -0
  6. data/erl_src/riak_search_test_backend.beam +0 -0
  7. data/erl_src/riak_search_test_backend.erl +175 -0
  8. data/lib/active_support/cache/riak_store.rb +2 -0
  9. data/lib/riak.rb +21 -0
  10. data/lib/riak/bucket.rb +215 -0
  11. data/lib/riak/cache_store.rb +84 -0
  12. data/lib/riak/client.rb +415 -0
  13. data/lib/riak/client/beefcake/messages.rb +147 -0
  14. data/lib/riak/client/beefcake/object_methods.rb +92 -0
  15. data/lib/riak/client/beefcake_protobuffs_backend.rb +176 -0
  16. data/lib/riak/client/excon_backend.rb +65 -0
  17. data/lib/riak/client/http_backend.rb +203 -0
  18. data/lib/riak/client/http_backend/configuration.rb +46 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +94 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +218 -0
  23. data/lib/riak/client/net_http_backend.rb +79 -0
  24. data/lib/riak/client/protobuffs_backend.rb +97 -0
  25. data/lib/riak/client/pump.rb +30 -0
  26. data/lib/riak/client/search.rb +94 -0
  27. data/lib/riak/core_ext.rb +6 -0
  28. data/lib/riak/core_ext/blank.rb +53 -0
  29. data/lib/riak/core_ext/extract_options.rb +7 -0
  30. data/lib/riak/core_ext/json.rb +15 -0
  31. data/lib/riak/core_ext/slice.rb +18 -0
  32. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  33. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  34. data/lib/riak/core_ext/to_param.rb +31 -0
  35. data/lib/riak/encoding.rb +6 -0
  36. data/lib/riak/failed_request.rb +81 -0
  37. data/lib/riak/i18n.rb +3 -0
  38. data/lib/riak/json.rb +28 -0
  39. data/lib/riak/link.rb +85 -0
  40. data/lib/riak/locale/en.yml +48 -0
  41. data/lib/riak/map_reduce.rb +206 -0
  42. data/lib/riak/map_reduce/filter_builder.rb +94 -0
  43. data/lib/riak/map_reduce/phase.rb +98 -0
  44. data/lib/riak/map_reduce_error.rb +7 -0
  45. data/lib/riak/robject.rb +290 -0
  46. data/lib/riak/search.rb +3 -0
  47. data/lib/riak/serializers.rb +74 -0
  48. data/lib/riak/stamp.rb +77 -0
  49. data/lib/riak/test_server.rb +252 -0
  50. data/lib/riak/util/escape.rb +45 -0
  51. data/lib/riak/util/fiber1.8.rb +48 -0
  52. data/lib/riak/util/headers.rb +53 -0
  53. data/lib/riak/util/multipart.rb +52 -0
  54. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  55. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  56. data/lib/riak/util/translation.rb +19 -0
  57. data/lib/riak/walk_spec.rb +105 -0
  58. data/riak-client.gemspec +55 -0
  59. data/seomoz-riak-client.gemspec +55 -0
  60. data/spec/fixtures/cat.jpg +0 -0
  61. data/spec/fixtures/multipart-blank.txt +7 -0
  62. data/spec/fixtures/multipart-mapreduce.txt +10 -0
  63. data/spec/fixtures/multipart-with-body.txt +16 -0
  64. data/spec/fixtures/server.cert.crt +15 -0
  65. data/spec/fixtures/server.cert.key +15 -0
  66. data/spec/fixtures/test.pem +1 -0
  67. data/spec/integration/riak/cache_store_spec.rb +154 -0
  68. data/spec/integration/riak/http_backends_spec.rb +58 -0
  69. data/spec/integration/riak/protobuffs_backends_spec.rb +32 -0
  70. data/spec/integration/riak/test_server_spec.rb +161 -0
  71. data/spec/riak/beefcake_protobuffs_backend_spec.rb +59 -0
  72. data/spec/riak/bucket_spec.rb +205 -0
  73. data/spec/riak/client_spec.rb +517 -0
  74. data/spec/riak/core_ext/to_param_spec.rb +15 -0
  75. data/spec/riak/escape_spec.rb +69 -0
  76. data/spec/riak/excon_backend_spec.rb +64 -0
  77. data/spec/riak/headers_spec.rb +38 -0
  78. data/spec/riak/http_backend/configuration_spec.rb +51 -0
  79. data/spec/riak/http_backend/object_methods_spec.rb +217 -0
  80. data/spec/riak/http_backend/transport_methods_spec.rb +117 -0
  81. data/spec/riak/http_backend_spec.rb +269 -0
  82. data/spec/riak/link_spec.rb +71 -0
  83. data/spec/riak/map_reduce/filter_builder_spec.rb +32 -0
  84. data/spec/riak/map_reduce/phase_spec.rb +136 -0
  85. data/spec/riak/map_reduce_spec.rb +310 -0
  86. data/spec/riak/multipart_spec.rb +23 -0
  87. data/spec/riak/net_http_backend_spec.rb +16 -0
  88. data/spec/riak/robject_spec.rb +427 -0
  89. data/spec/riak/search_spec.rb +178 -0
  90. data/spec/riak/serializers_spec.rb +93 -0
  91. data/spec/riak/stamp_spec.rb +54 -0
  92. data/spec/riak/stream_parser_spec.rb +53 -0
  93. data/spec/riak/walk_spec_spec.rb +195 -0
  94. data/spec/spec_helper.rb +39 -0
  95. data/spec/support/drb_mock_server.rb +39 -0
  96. data/spec/support/http_backend_implementation_examples.rb +266 -0
  97. data/spec/support/integration_setup.rb +10 -0
  98. data/spec/support/mock_server.rb +81 -0
  99. data/spec/support/mocks.rb +4 -0
  100. data/spec/support/test_server.yml.example +2 -0
  101. data/spec/support/unified_backend_examples.rb +255 -0
  102. 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
@@ -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
@@ -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"
@@ -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
@@ -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).