elasticsearch-rails2 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,125 @@
1
+ module Elasticsearch
2
+
3
+ module Rails2
4
+ # Provides methods for getting and setting index name
5
+ #
6
+ module Naming
7
+
8
+ module ClassMethods
9
+
10
+ # Get or set the name of the index
11
+ #
12
+ # @example Set the index name for the `Building` model
13
+ #
14
+ # class Building
15
+ # index_name "buildings-#{Rails.env}"
16
+ # end
17
+ #
18
+ # @example Set the index name for the `Building` model and re-evaluate it on each call
19
+ #
20
+ # class Building
21
+ # index_name { "buildings-#{Time.now.year}" }
22
+ # end
23
+ #
24
+ # @example Directly set the index name for the `Building` model
25
+ #
26
+ # Building.index_name "buildings-#{Rails.env}"
27
+ #
28
+ #
29
+ def index_name name=nil, &block
30
+ if name || block_given?
31
+ return (@index_name = name || block)
32
+ end
33
+
34
+ if @index_name.respond_to?(:call)
35
+ @index_name.call
36
+ else
37
+ @index_name || Elasticsearch::Rails2.index_name || "#{self.model_name.collection}_index"
38
+ end
39
+ end
40
+
41
+ # Set the index name
42
+ #
43
+ # @see index_name
44
+ def index_name=(name)
45
+ @index_name = name
46
+ end
47
+
48
+ # Get or set the document type
49
+ #
50
+ # @example Set the document type for the `Building` model
51
+ #
52
+ # class Building
53
+ # document_type "my-building"
54
+ # end
55
+ #
56
+ # @example Directly set the document type for the `Building` model
57
+ #
58
+ # Building.document_type "my-building"
59
+ #
60
+ def document_type name=nil
61
+ @document_type = name || @document_type || self.model_name.collection
62
+ end
63
+
64
+
65
+ # Set the document type
66
+ #
67
+ # @see document_type
68
+ #
69
+ def document_type=(name)
70
+ @document_type = name
71
+ end
72
+ end
73
+
74
+ module InstanceMethods
75
+
76
+ # Get or set the index name for the model instance
77
+ #
78
+ # @example Set the index name for an instance of the `Building` model
79
+ #
80
+ # @building.index_name "buildings-#{@building.sourceid}"
81
+ #
82
+ #
83
+ def index_name name=nil, &block
84
+ if name || block_given?
85
+ return (@index_name = name || block)
86
+ end
87
+
88
+ if @index_name.respond_to?(:call)
89
+ @index_name.call
90
+ else
91
+ @index_name || self.class.index_name
92
+ end
93
+ end
94
+
95
+ # Set the index name
96
+ #
97
+ # @see index_name
98
+ def index_name=(name)
99
+ @index_name = name
100
+ end
101
+
102
+ # Get or set the document type
103
+ #
104
+ # @example Set the document type for an instance of the `Building` model
105
+ #
106
+ # @bulding.document_type "my-building"
107
+ #
108
+ #
109
+ def document_type name=nil
110
+ @document_type = name || @document_type || self.class.document_type
111
+ end
112
+
113
+
114
+ # Set the document type
115
+ #
116
+ # @see document_type
117
+ #
118
+ def document_type=(name)
119
+ @document_type = name
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,77 @@
1
+ module Elasticsearch
2
+ module Rails2
3
+ # Contains modules and classes for wrapping the response from Elasticsearch
4
+ #
5
+ module Response
6
+
7
+ # Encapsulate the response returned from the Elasticsearch client
8
+ #
9
+ # Implements Enumerable and forwards its methods to the {#results} object.
10
+ #
11
+ class Response
12
+ attr_reader :klass, :search, :response,
13
+ :took, :timed_out, :shards
14
+
15
+ include Enumerable
16
+
17
+ delegate :each, :empty?, :size, :slice, :[], :to_ary, to: :results
18
+
19
+ def initialize(klass, search, options={})
20
+ @klass = klass
21
+ @search = search
22
+ end
23
+
24
+ # Returns the Elasticsearch response
25
+ #
26
+ # @return [Hash]
27
+ #
28
+ def response
29
+ @response ||= begin
30
+ Hashie::Mash.new(search.execute!)
31
+ end
32
+ end
33
+
34
+ # Returns the collection of "hits" from Elasticsearch
35
+ #
36
+ # @return [Results]
37
+ #
38
+ def results
39
+ @results ||= Results.new(klass, self)
40
+ end
41
+
42
+ # Returns the collection of records from the database
43
+ #
44
+ # @return [Records]
45
+ #
46
+ def records
47
+ @records ||= Records.new(klass, self)
48
+ end
49
+
50
+ # Returns the "took" time
51
+ #
52
+ def took
53
+ response['took']
54
+ end
55
+
56
+ # Returns whether the response timed out
57
+ #
58
+ def timed_out
59
+ response['timed_out']
60
+ end
61
+
62
+ # Returns the statistics on shards
63
+ #
64
+ def shards
65
+ Hashie::Mash.new(response['_shards'])
66
+ end
67
+
68
+ # Returns whether the response scroll id
69
+ #
70
+ def scroll_id
71
+ response['_scroll_id']
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,62 @@
1
+ module Elasticsearch
2
+ module Rails2
3
+ module Response
4
+
5
+ # Encapsulates the "hit" returned from the Elasticsearch client
6
+ #
7
+ # Wraps the raw Hash with in a `Hashie::Mash` instance, providing
8
+ # access to the Hash properties by calling Ruby methods.
9
+ #
10
+ # @see https://github.com/intridea/hashie
11
+ #
12
+ class Result
13
+
14
+ # @param attributes [Hash] A Hash with document properties
15
+ #
16
+ def initialize(attributes={})
17
+ @result = Hashie::Mash.new(attributes)
18
+ end
19
+
20
+ # Return document `_id` as `id`
21
+ #
22
+ def id
23
+ @result['_id']
24
+ end
25
+
26
+ # Return document `_type` as `_type`
27
+ #
28
+ def type
29
+ @result['_type']
30
+ end
31
+
32
+ # Delegate methods to `@result` or `@result._source`
33
+ #
34
+ def method_missing(name, *arguments)
35
+ case
36
+ when name.to_s.end_with?('?')
37
+ @result.__send__(name, *arguments) || ( @result._source && @result._source.__send__(name, *arguments) )
38
+ when @result.respond_to?(name)
39
+ @result.__send__ name, *arguments
40
+ when @result._source && @result._source.respond_to?(name)
41
+ @result._source.__send__ name, *arguments
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ # Respond to methods from `@result` or `@result._source`
48
+ #
49
+ def respond_to?(method_name, include_private = false)
50
+ @result.respond_to?(method_name.to_sym) || \
51
+ @result._source && @result._source.respond_to?(method_name.to_sym) || \
52
+ super
53
+ end
54
+
55
+ def as_json(options={})
56
+ @result.as_json(options)
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,52 @@
1
+ module Elasticsearch
2
+ module Rails2
3
+ module Response
4
+
5
+ # Encapsulates the collection of documents returned from Elasticsearch
6
+ #
7
+ # Implements Enumerable and forwards its methods to the {#results} object.
8
+ #
9
+ class Results
10
+ attr_reader :klass, :response
11
+
12
+ include Enumerable
13
+
14
+ delegate :each, :empty?, :size, :slice, :[], :to_a, :to_ary, to: :results
15
+
16
+ # @param klass [Class] The name of the model class
17
+ # @param response [Hash] The full response returned from Elasticsearch client
18
+ # @param options [Hash] Optional parameters
19
+ #
20
+ def initialize(klass, response, options={})
21
+ @klass = klass
22
+ @response = response
23
+ end
24
+
25
+ # Returns the {Results} collection
26
+ #
27
+ def results
28
+ @results = response.response['hits']['hits'].map { |hit| Result.new(hit) }
29
+ end
30
+
31
+ # Returns the total number of hits
32
+ #
33
+ def total
34
+ response.response['hits']['total']
35
+ end
36
+
37
+ # Returns the max_score
38
+ #
39
+ def max_score
40
+ response.response['hits']['max_score']
41
+ end
42
+
43
+ # Returns the hit IDs
44
+ #
45
+ def ids
46
+ response.response['hits']['hits'].map { |hit| hit['_id'] }
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,120 @@
1
+ module Elasticsearch
2
+ module Rails2
3
+
4
+ # Contains functionality related to searching.
5
+ #
6
+ module Searching
7
+
8
+ # Wraps a search request definition
9
+ #
10
+ class SearchRequest
11
+ attr_reader :klass, :definition, :options
12
+
13
+ # @param klass [Class] The class of the model
14
+ # @param query_or_payload [String,Hash,Object] The search request definition
15
+ # (string, JSON, Hash, or object responding to `to_hash`)
16
+ # @param options [Hash] Optional parameters to be passed to the Elasticsearch client
17
+ #
18
+ def initialize(klass, query_or_payload, options={})
19
+ @klass = klass
20
+ @options = options
21
+
22
+ __index_name = options[:index] || klass.index_name
23
+ __document_type = options[:type] || klass.document_type
24
+
25
+ case
26
+ # search query: ...
27
+ when query_or_payload.respond_to?(:to_hash)
28
+ body = query_or_payload.to_hash
29
+
30
+ # search '{ "query" : ... }'
31
+ when query_or_payload.is_a?(String) && query_or_payload =~ /^\s*{/
32
+ body = query_or_payload
33
+
34
+ # search '...'
35
+ else
36
+ q = query_or_payload
37
+ end
38
+
39
+ if body
40
+ @definition = { index: __index_name, type: __document_type, body: body }.update options
41
+ else
42
+ @definition = { index: __index_name, type: __document_type, q: q }.update options
43
+ end
44
+ end
45
+
46
+ # Performs the request and returns the response from client
47
+ #
48
+ # @return [Hash] The response from Elasticsearch
49
+ #
50
+ def execute!
51
+ klass.client.search(@definition)
52
+ end
53
+ end
54
+
55
+ module ClassMethods
56
+
57
+ # Provides a `search` method for the model to easily search within an index/type
58
+ # corresponding to the model settings.
59
+ #
60
+ # @param query_or_payload [String,Hash,Object] The search request definition
61
+ # (string, JSON, Hash, or object responding to `to_hash`)
62
+ # @param options [Hash] Optional parameters to be passed to the Elasticsearch client
63
+ #
64
+ # @return [Elasticsearch::Rails2::Response]
65
+ #
66
+ # @example Simple search in `Article`
67
+ #
68
+ # Article.search 'foo'
69
+ #
70
+ # @example Search using a search definition as a Hash
71
+ #
72
+ # response = Article.search \
73
+ # query: {
74
+ # match: {
75
+ # title: 'foo'
76
+ # }
77
+ # },
78
+ # highlight: {
79
+ # fields: {
80
+ # title: {}
81
+ # }
82
+ # }
83
+ #
84
+ # response.results.first.title
85
+ # # => "Foo"
86
+ #
87
+ # response.results.first.highlight.title
88
+ # # => ["<em>Foo</em>"]
89
+ #
90
+ # response.records.first.title
91
+ # # Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" IN (1, 3)
92
+ # # => "Foo"
93
+ #
94
+ # @example Search using a search definition as a JSON string
95
+ #
96
+ # Article.search '{"query" : { "match_all" : {} }}'
97
+ #
98
+ def search(query_or_payload, options={})
99
+ search = SearchRequest.new(self, query_or_payload, options)
100
+ Response::Response.new(self, search)
101
+ end
102
+
103
+ # Scan and scroll all ids
104
+ # Useful to do a SQL query with IN(...) operator
105
+ #
106
+ def scan_all_ids(query_or_payload, options={})
107
+ ids = []
108
+ scroll = options[:scroll]
109
+ search_response = search(query_or_payload, options.update(search_type: 'scan'))
110
+ response = search_response.response
111
+ while response = client.scroll(scroll_id: response['_scroll_id'], scroll: scroll) and !response['hits']['hits'].empty? do
112
+ response['hits']['hits'].each { |r| ids << r['_id']}
113
+ end
114
+ ids
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,5 @@
1
+ module Elasticsearch
2
+ module Rails2
3
+ VERSION = "0.0.1"
4
+ end
5
+ end