slingshot-rb 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"title" : "One", "tags" : ["ruby"]}
@@ -0,0 +1 @@
1
+ {"title" : "Two", "tags" : ["ruby", "python"]}
@@ -0,0 +1 @@
1
+ {"title" : "Three", "tags" : ["java"]}
@@ -0,0 +1 @@
1
+ {"title" : "Four", "tags" : ["erlang"]}
@@ -0,0 +1 @@
1
+ {"title" : "Five", "tags" : ["javascript", "java"]}
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class FacetsIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Facets" do
9
+
10
+ should "return results scoped to current query" do
11
+ q = 'tags:ruby'
12
+ s = Slingshot.search('articles-test') do
13
+ query { string q }
14
+ facet 'tags' do
15
+ terms :tags
16
+ end
17
+ end
18
+ facets = s.results.facets['tags']['terms']
19
+ assert_equal 2, facets.count
20
+ assert_equal 'ruby', facets.first['term']
21
+ assert_equal 2, facets.first['count']
22
+ end
23
+
24
+ should "allow to specify global facets and query-scoped facets" do
25
+ q = 'tags:ruby'
26
+ s = Slingshot.search('articles-test') do
27
+ query { string q }
28
+ facet 'scoped-tags' do
29
+ terms :tags
30
+ end
31
+ facet 'global-tags', :global => true do
32
+ terms :tags
33
+ end
34
+ end
35
+
36
+ scoped_facets = s.results.facets['scoped-tags']['terms']
37
+ global_facets = s.results.facets['global-tags']['terms']
38
+
39
+ assert_equal 2, scoped_facets.count
40
+ assert_equal 5, global_facets.count
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class QueryStringIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Searching for query string" do
9
+
10
+ should "find article by title" do
11
+ q = 'title:one'
12
+ assert_equal 1, search(q).results.count
13
+ assert_equal 'One', search(q).results.first['_source']['title']
14
+ end
15
+
16
+ should "find articles by title with boosting" do
17
+ q = 'title:one^100 OR title:two'
18
+ assert_equal 2, search(q).results.count
19
+ assert_equal 'One', search(q).results.first['_source']['title']
20
+ end
21
+
22
+ should "find articles by tags" do
23
+ q = 'tags:ruby AND tags:python'
24
+ assert_equal 1, search(q).results.count
25
+ assert_equal 'Two', search(q).results.first['_source']['title']
26
+ end
27
+
28
+ should "find any article with tags" do
29
+ q = 'tags:ruby OR tags:python OR tags:java'
30
+ assert_equal 4, search(q).results.count
31
+ end
32
+
33
+ end
34
+
35
+ private
36
+
37
+ def search(q)
38
+ Slingshot.search('articles-test') { query { string q } }
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class SortIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Sort" do
9
+
10
+ should "sort by title" do
11
+ q = '*'
12
+ s = Slingshot.search('articles-test') do
13
+ query { string q }
14
+ sort { title }
15
+ end
16
+
17
+ assert_equal 5, s.results.count
18
+ assert_equal 'Five', s.results.first['_source']['title']
19
+ end
20
+
21
+ should "sort by title, descending" do
22
+ q = '*'
23
+ s = Slingshot.search('articles-test') do
24
+ query { string q }
25
+ sort { title :desc }
26
+ end
27
+
28
+ assert_equal 5, s.results.count
29
+ assert_equal 'Two', s.results.first['_source']['title']
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+ require 'turn' unless ENV["TM_FILEPATH"]
6
+ require 'pathname'
7
+
8
+ require 'slingshot'
9
+
10
+ class Test::Unit::TestCase
11
+
12
+ def fixtures_path
13
+ Pathname( File.expand_path( 'fixtures', File.dirname(__FILE__) ) )
14
+ end
15
+
16
+ def fixture_file(path)
17
+ File.read File.expand_path( path, fixtures_path )
18
+ end
19
+
20
+ end
21
+
22
+ module Test::Integration
23
+ URL = "http://localhost:9200"
24
+
25
+ def setup
26
+ begin
27
+ ::RestClient.get URL
28
+ rescue Errno::ECONNREFUSED
29
+ abort "\n\n#{'-'*87}\n[ABORTED] You have to run ElasticSearch on #{URL} for integration tests\n#{'-'*87}\n\n"
30
+ end
31
+
32
+ ::RestClient.delete "#{URL}/articles-test" rescue nil
33
+ ::RestClient.post "#{URL}/articles-test", ''
34
+ fixtures_path.join('articles').entries.each do |f|
35
+ filename = f.to_s
36
+ next if filename =~ /^\./
37
+ ::RestClient.put "#{URL}/articles-test/article/#{File.basename(filename, '.*')}",
38
+ fixtures_path.join('articles').join(f).read
39
+ end
40
+ ::RestClient.post "#{URL}/articles-test/_refresh", ''
41
+ end
42
+
43
+ def teardown
44
+ ::RestClient.delete "#{URL}/articles-test" rescue nil
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class ClientTest < Test::Unit::TestCase
6
+
7
+ context "Base" do
8
+ setup { @http ||= Client::Base.new }
9
+
10
+ should "have abstract methods" do
11
+ assert_raise(ArgumentError) { @http.post }
12
+ assert_raise(ArgumentError) { @http.post 'URL' }
13
+ assert_raise(NoMethodError) { @http.post 'URL', 'DATA' }
14
+
15
+ assert_raise(ArgumentError) { @http.delete }
16
+ assert_raise(NoMethodError) { @http.delete 'URL' }
17
+ end
18
+ end
19
+
20
+ context "RestClient" do
21
+
22
+ should "be default" do
23
+ assert_equal Client::RestClient, Configuration.client
24
+ end
25
+
26
+ should "respond to post and delete" do
27
+ assert_respond_to Client::RestClient, :post
28
+ assert_respond_to Client::RestClient, :delete
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class ConfigurationTest < Test::Unit::TestCase
6
+
7
+ context "Configuration" do
8
+ setup do
9
+ Configuration.instance_variable_set(:@url, nil)
10
+ Configuration.instance_variable_set(:@client, nil)
11
+ end
12
+
13
+ should "return default URL" do
14
+ assert_equal 'http://localhost:9200', Configuration.url
15
+ end
16
+
17
+ should "allow setting and retrieving the URL" do
18
+ assert_nothing_raised { Configuration.url 'http://example.com' }
19
+ assert_equal 'http://example.com', Configuration.url
20
+ end
21
+
22
+ should "return default client" do
23
+ assert_equal Client::RestClient, Configuration.client
24
+ end
25
+
26
+ should "allow setting and retrieving the client" do
27
+ assert_nothing_raised { Configuration.client Client::Base }
28
+ assert_equal Client::Base, Configuration.client
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class IndexTest < Test::Unit::TestCase
6
+
7
+ context "Index" do
8
+
9
+ setup do
10
+ @index = Slingshot::Index.new 'dummy'
11
+ end
12
+
13
+ should "create new index" do
14
+ Configuration.client.expects(:post).returns('{"ok":true,"acknowledged":true}')
15
+ assert @index.create
16
+ end
17
+
18
+ should "not raise exception and just return false when trying to create existing index" do
19
+ Configuration.client.expects(:post).raises(RestClient::BadRequest)
20
+ assert_nothing_raised { assert ! @index.create }
21
+ end
22
+
23
+ should "delete index" do
24
+ Configuration.client.expects(:delete).returns('{"ok":true,"acknowledged":true}')
25
+ assert @index.delete
26
+ end
27
+
28
+ should "not raise exception and just return false when deleting non-existing index" do
29
+ Configuration.client.expects(:delete).returns('{"error":"[articles] missing"}')
30
+ assert_nothing_raised { assert ! @index.delete }
31
+ Configuration.client.expects(:delete).raises(RestClient::BadRequest)
32
+ assert_nothing_raised { assert ! @index.delete }
33
+ end
34
+
35
+ should "refresh the index" do
36
+ Configuration.client.expects(:post).returns('{"ok":true,"_shards":{}}')
37
+ assert_nothing_raised { assert @index.refresh }
38
+ end
39
+
40
+ context "when storing" do
41
+
42
+ should "properly set type from args" do
43
+ Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/", '{"title":"Test"}').returns('{"ok":true,"_id":"test"}').twice
44
+ @index.store 'article', :title => 'Test'
45
+ @index.store :article, :title => 'Test'
46
+ end
47
+
48
+ should "set default type" do
49
+ Configuration.client.expects(:post).with("#{Configuration.url}/dummy/document/", '{"title":"Test"}').returns('{"ok":true,"_id":"test"}')
50
+ @index.store :title => 'Test'
51
+ end
52
+
53
+ should "call #to_indexed_json on non-String documents" do
54
+ document = { :title => 'Test' }
55
+ Configuration.client.expects(:post).returns('{"ok":true,"_id":"test"}')
56
+ document.expects(:to_indexed_json)
57
+ @index.store document
58
+ end
59
+
60
+ should "raise error when storing neither String nor object with #to_indexed_json method" do
61
+ class MyDocument;end; document = MyDocument.new
62
+ assert_raise(ArgumentError) { @index.store document }
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class ResultsCollectionTest < Test::Unit::TestCase
6
+
7
+ context "Collection" do
8
+ setup do
9
+ @default_response = { 'hits' => { 'hits' => [1, 2, 3] } }
10
+ end
11
+
12
+ should "be iterable" do
13
+ assert_respond_to Results::Collection.new(@default_response), :each
14
+ assert_nothing_raised do
15
+ Results::Collection.new(@default_response).each { |item| item + 1 }
16
+ Results::Collection.new(@default_response).map { |item| item + 1 }
17
+ end
18
+ end
19
+
20
+ should "be initialized with parsed json" do
21
+ assert_nothing_raised do
22
+ collection = Results::Collection.new( @default_response )
23
+ assert_equal [1,2,3], collection.results
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot::Search
4
+
5
+ class FacetTest < Test::Unit::TestCase
6
+
7
+ context "Facet" do
8
+
9
+ should "be serialized to JSON" do
10
+ assert_respond_to Sort.new, :to_json
11
+ end
12
+
13
+ context "generally" do
14
+
15
+ should "encode facets with defaults for current query" do
16
+ assert_equal( { :foo => { :terms => {:field=>'bar'} } }.to_json, Facet.new('foo').terms(:bar).to_json )
17
+ end
18
+
19
+ should "encode facets as global" do
20
+ assert_equal( { :foo => { :terms => {:field=>'bar'}, :global => true } }.to_json,
21
+ Facet.new('foo', :global => true).terms(:bar).to_json )
22
+ end
23
+
24
+ should "encode facet options" do
25
+ assert_equal( { :foo => { :terms => {:field=>'bar'}, :size => 5 } }.to_json,
26
+ Facet.new('foo', :size => 5).terms(:bar).to_json )
27
+ assert_equal( { :foo => { :terms => {:field=>'bar'}, :size => 5 } }.to_json,
28
+ Facet.new('foo').terms(:bar, :size => 5).to_json )
29
+ end
30
+
31
+ should "encode facets when passed as a block" do
32
+ f = Facet.new('foo') do
33
+ terms :bar
34
+ end
35
+ assert_equal( { :foo => { :terms => {:field=>'bar'} } }.to_json, f.to_json )
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot::Search
4
+
5
+ class QueryTest < Test::Unit::TestCase
6
+
7
+ context "Query" do
8
+
9
+ should "be serialized to JSON" do
10
+ assert_respond_to Query.new, :to_json
11
+ end
12
+
13
+ should "allow search for single term" do
14
+ assert_equal( { :term => { :foo => 'bar' } }, Query.new.term(:foo, 'bar') )
15
+ end
16
+
17
+ should "allow search for multiple terms" do
18
+ assert_equal( { :terms => { :foo => ['bar', 'baz'] } }, Query.new.terms(:foo, ['bar', 'baz']) )
19
+ end
20
+
21
+ should "allow set minimum match when searching for multiple terms" do
22
+ assert_equal( { :terms => { :foo => ['bar', 'baz'], :minimum_match => 2 } },
23
+ Query.new.terms(:foo, ['bar', 'baz'], :minimum_match => 2) )
24
+ end
25
+
26
+ should "allow search with a query string" do
27
+ assert_equal( { :query_string => { :query => 'title:foo' } },
28
+ Query.new.string('title:foo') )
29
+ end
30
+
31
+ should "allow set default field when searching with a query string" do
32
+ assert_equal( { :query_string => { :query => 'foo', :default_field => 'title' } },
33
+ Query.new.string('foo', :default_field => 'title') )
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot::Search
4
+
5
+ class SortTest < Test::Unit::TestCase
6
+
7
+ context "Sort" do
8
+
9
+ should "be serialized to JSON" do
10
+ assert_respond_to Sort.new, :to_json
11
+ end
12
+
13
+ should "encode simple strings" do
14
+ assert_equal [:foo].to_json, Sort.new.foo.to_json
15
+ end
16
+
17
+ should "encode method arguments" do
18
+ assert_equal [:foo => 'desc'].to_json, Sort.new.foo('desc').to_json
19
+ end
20
+
21
+ should "encode hash" do
22
+ assert_equal [ :foo => { :reverse => true } ].to_json, Sort.new.foo(:reverse => true).to_json
23
+ end
24
+
25
+ should "encode multiple sort fields" do
26
+ assert_equal [:foo, :bar].to_json, Sort.new.foo.bar.to_json
27
+ end
28
+
29
+ should "encode fields when passed as a block to constructor" do
30
+ s = Sort.new do
31
+ foo
32
+ bar 'desc'
33
+ _score
34
+ end
35
+ assert_equal [ :foo, {:bar => 'desc'}, :_score ].to_json, s.to_json
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,106 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class SearchTest < Test::Unit::TestCase
6
+
7
+ context "Search" do
8
+
9
+ should "be initialized with index/indices" do
10
+ assert_raise(ArgumentError) { Search::Search.new }
11
+ end
12
+
13
+ should "have the query method" do
14
+ assert_respond_to Search::Search.new('index'), :query
15
+ end
16
+
17
+ should "store indices as an array" do
18
+ s = Search::Search.new('index1') do;end
19
+ assert_equal ['index1'], s.indices
20
+
21
+ s = Search::Search.new('index1', 'index2') do;end
22
+ assert_equal ['index1', 'index2'], s.indices
23
+ end
24
+
25
+ should "return curl snippet for debugging" do
26
+ s = Search::Search.new('index') do
27
+ query { string 'title:foo' }
28
+ end
29
+ assert_equal %q|curl -X POST "http://localhost:9200/index/_search?pretty=true" -d | +
30
+ %q|'{"query":{"query_string":{"query":"title:foo"}}}'|,
31
+ s.to_curl
32
+ end
33
+
34
+ should "allow chaining" do
35
+ assert_nothing_raised do
36
+ Search::Search.new('index').query { }.sort { title 'desc' }.size(5).sort { name 'asc' }.from(1)
37
+ end
38
+ end
39
+
40
+ should "perform the search" do
41
+ Configuration.client.expects(:post).returns('{"hits":[]}')
42
+ Results::Collection.expects(:new).returns([])
43
+ s = Search::Search.new('index')
44
+ s.perform
45
+ assert_not_nil s.results
46
+ assert_not_nil s.response
47
+ end
48
+
49
+ context "sort" do
50
+
51
+ should "allow sorting by multiple fields" do
52
+ s = Search::Search.new('index') do
53
+ sort do
54
+ title 'desc'
55
+ _score
56
+ end
57
+ end
58
+ hash = JSON.load( s.to_json )
59
+ assert_equal [{'title' => 'desc'}, '_score'], hash['sort']
60
+ end
61
+
62
+ end
63
+
64
+ context "facets" do
65
+
66
+ should "allow searching for facets" do
67
+ s = Search::Search.new('index') do
68
+ facet('foo1') { terms :bar, :global => true }
69
+ facet('foo2', :global => true) { terms :bar }
70
+ facet('foo3') { terms :baz }
71
+ end
72
+ assert_equal 3, s.facets.keys.size
73
+ assert_not_nil s.facets['foo1']
74
+ assert_not_nil s.facets['foo2']
75
+ assert_not_nil s.facets['foo3']
76
+ end
77
+
78
+ end
79
+
80
+ context "with from/size" do
81
+
82
+ should "set the values in request" do
83
+ s = Search::Search.new('index') do
84
+ size 5
85
+ from 3
86
+ end
87
+ hash = JSON.load( s.to_json )
88
+ assert_equal 5, hash['size']
89
+ assert_equal 3, hash['from']
90
+ end
91
+
92
+ should "set the fields limit in request" do
93
+ s = Search::Search.new('index') do
94
+ fields :title
95
+ end
96
+ hash = JSON.load( s.to_json )
97
+ assert_equal 'title', hash['fields']
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ module Slingshot
4
+
5
+ class SlingshotTest < Test::Unit::TestCase
6
+
7
+ context "Slingshot" do
8
+
9
+ should "have the DSL methods available" do
10
+ assert_respond_to Slingshot, :search
11
+ assert_respond_to Slingshot, :index
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ end