require 'spec_helper' require 'rexml/document' describe "Search features" do describe Riak::Client do before :each do @client = Riak::Client.new @http = mock(Riak::Client::HTTPBackend) @client.stub!(:http).and_yield(@http) end describe "searching" do it "should search the default index" do @http.should_receive(:search).with(nil, "foo", {}).and_return({}) @client.search("foo") end it "should search the default index with additional options" do @http.should_receive(:search).with(nil, 'foo', 'rows' => 30).and_return({}) @client.search("foo", 'rows' => 30) end it "should search the specified index" do @http.should_receive(:search).with('search', 'foo', {}).and_return({}) @client.search("search", "foo") end end describe "indexing documents" do it "should update the default index" do doc = {'field' => "value", 'id' => 1} expect_update_body do |body| root = REXML::Document.new(body).root root.name.should == 'add' root.elements.should have(1).item root.elements[1].each_element do |el| case el.attributes['name'] when 'field' el.text.should == 'value' when 'id' el.text.should == '1' else fail "Spurious element in #{root.elements[1]}! #{el}" end end end @client.index(doc) end it "should update the specified index" do doc = {'field' => "value", 'id' => 1} expect_update_body(nil, "foo") do |body| root = REXML::Document.new(body).root root.name.should == 'add' root.elements.should have(1).item root.elements[1].each_element do |el| case el.attributes['name'] when 'field' el.text.should == 'value' when 'id' el.text.should == '1' else fail "Spurious element in #{root.elements[1]}! #{el}" end end end @client.index("foo", doc) end it "should raise an error when documents do not contain an id" do @http.stub!(:update_search_index).and_return(true) lambda { @client.index({:field => "value"}) }.should raise_error(ArgumentError) lambda { @client.index({:id => 1, :field => "value"}) }.should_not raise_error(ArgumentError) end it "should include multiple documents in the request" do docs = {'field' => "value", 'id' => 1}, {'foo' => "bar", 'id' => 2} expect_update_body do |body| root = REXML::Document.new(body).root root.name.should == 'add' docs = root.elements docs.each do |d| d.name.should == 'doc' d.elements.size.should == 2 end docs[1].each_element do |el| case el.attributes['name'] when 'field' el.text.should == 'value' when 'id' el.text.should == '1' else fail "Spurious element in #{docs[0]}! #{el}" end end docs[2].each_element do |el| case el.attributes['name'] when 'foo' el.text.should == 'bar' when 'id' el.text.should == '2' else fail "Spurious element in #{docs[1]}! #{el}" end end end @client.index(*docs) end end describe "removing documents" do it "should remove documents from the default index" do expect_update_body('1') @client.remove({:id => 1}) end it "should remove documents from the specified index" do expect_update_body('1', 'foo') @client.remove("foo", {:id => 1}) end it "should raise an error when document specifications don't include an id or query" do @http.stub!(:update_search_index).and_return({:code => 200}) lambda { @client.remove({:foo => "bar"}) }.should raise_error(ArgumentError) lambda { @client.remove({:id => 1}) }.should_not raise_error end it "should build a Solr request" do expect_update_body('1') @client.remove(:id => 1) expect_update_body('title:old') @client.remove(:query => "title:old") end it "should include multiple specs in the request" do expect_update_body('1title:old') @client.remove({:id => 1}, {:query => "title:old"}) end end def expect_update_body(body=nil, index=nil) if block_given? @http.should_receive(:update_search_index).with(index, kind_of(String)) do |i, b| yield b end else @http.should_receive(:update_search_index).with(index, body) end end end describe Riak::Bucket do before :each do @client = Riak::Client.new @bucket = Riak::Bucket.new(@client, "foo") end def load_without_index_hook @bucket.instance_variable_set(:@props, {"precommit" => [], "search" => false}) end def load_with_index_hook @bucket.instance_variable_set(:@props, {"precommit" => [{"mod" => "riak_search_kv_hook", "fun" => "precommit"}], "search" => true}) end it "should detect whether the indexing hook is installed" do load_without_index_hook @bucket.is_indexed?.should be_false load_with_index_hook @bucket.is_indexed?.should be_true end describe "enabling indexing" do it "should add the index hook when not present" do load_without_index_hook @bucket.should_receive(:props=).with({"precommit" => [Riak::Bucket::SEARCH_PRECOMMIT_HOOK], "search" => true}) @bucket.enable_index! end it "should not modify the precommit when the hook is present" do load_with_index_hook @bucket.should_not_receive(:props=) @bucket.enable_index! end end describe "disabling indexing" do it "should remove the index hook when present" do load_with_index_hook @bucket.should_receive(:props=).with({"precommit" => [], "search" => false}) @bucket.disable_index! end it "should not modify the precommit when the hook is missing" do load_without_index_hook @bucket.should_not_receive(:props=) @bucket.disable_index! end end end describe Riak::MapReduce do before :each do @client = Riak::Client.new @mr = Riak::MapReduce.new(@client) end describe "using a search query as inputs" do it "should accept a bucket name and query" do @mr.search("foo", "bar OR baz") @mr.inputs.should == {:module => "riak_search", :function => "mapred_search", :arg => ["foo", "bar OR baz"]} end it "should accept a Riak::Bucket and query" do @mr.search(Riak::Bucket.new(@client, "foo"), "bar OR baz") @mr.inputs.should == {:module => "riak_search", :function => "mapred_search", :arg => ["foo", "bar OR baz"]} end it "should emit the Erlang function and arguments" do @mr.search("foo", "bar OR baz") @mr.to_json.should include('"inputs":{') @mr.to_json.should include('"module":"riak_search"') @mr.to_json.should include('"function":"mapred_search"') @mr.to_json.should include('"arg":["foo","bar OR baz"]') end end end end