slingshot-rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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