gummi 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/gummi.gemspec +7 -6
- data/lib/gummi.rb +32 -26
- data/lib/gummi/api.rb +3 -1
- data/lib/gummi/db_layer/default_index.rb +15 -0
- data/lib/gummi/db_layer/document.rb +206 -0
- data/lib/gummi/db_layer/document/attributes.rb +40 -0
- data/lib/gummi/db_layer/document/object.rb +15 -0
- data/lib/gummi/db_layer/document/search/filtered.rb +42 -0
- data/lib/gummi/db_layer/document/search/raw.rb +12 -0
- data/lib/gummi/db_layer/document/search/result.rb +34 -0
- data/lib/gummi/db_layer/document/search/searching.rb +51 -0
- data/lib/gummi/db_layer/fields/boolean.rb +13 -0
- data/lib/gummi/db_layer/fields/integer.rb +16 -0
- data/lib/gummi/db_layer/fields/keyword.rb +15 -0
- data/lib/gummi/db_layer/fields/ngram_and_plain.rb +20 -0
- data/lib/gummi/db_layer/fields/path_hierarchy.rb +15 -0
- data/lib/gummi/db_layer/fields/positive_integer.rb +21 -0
- data/lib/gummi/db_layer/fields/sanitized_string.rb +30 -0
- data/lib/gummi/db_layer/fields/string.rb +17 -0
- data/lib/gummi/db_layer/fields/time.rb +17 -0
- data/lib/gummi/db_layer/index.rb +150 -0
- data/lib/gummi/entity_layer/entity.rb +22 -0
- data/lib/gummi/errors.rb +7 -0
- data/lib/gummi/repository_layer/repository.rb +39 -0
- data/lib/gummi/repository_layer/repository/result.rb +42 -0
- data/lib/gummi/version.rb +1 -1
- data/lib/repobahn/repository.rb +25 -33
- data/lib/repobahn/repository/active_record.rb +17 -0
- data/spec/fixtures/admin/auto.rb +6 -0
- data/spec/fixtures/admin/cars.rb +12 -0
- data/spec/fixtures/admin/countries.rb +9 -0
- data/spec/fixtures/admin/country.rb +6 -0
- data/spec/fixtures/admin/db/country.rb +7 -0
- data/spec/fixtures/admin/db/vehicle.rb +7 -0
- data/spec/fixtures/cities.rb +7 -0
- data/spec/fixtures/city.rb +6 -0
- data/spec/fixtures/db/animal.rb +9 -0
- data/spec/fixtures/db/boat.rb +9 -0
- data/spec/fixtures/db/car.rb +9 -0
- data/spec/fixtures/db/city.rb +8 -0
- data/spec/fixtures/db/enemy.rb +10 -0
- data/spec/fixtures/db/game.rb +7 -0
- data/spec/fixtures/db/person.rb +15 -0
- data/spec/fixtures/db/rating.rb +11 -0
- data/spec/fixtures/db/ship.rb +18 -0
- data/spec/{models → fixtures}/people.rb +6 -2
- data/spec/{models → fixtures}/person.rb +3 -2
- data/spec/lib/gummi/db_layer/document_spec.rb +124 -0
- data/spec/lib/gummi/{entity_spec.rb → entity_layer/entity_spec.rb} +3 -1
- data/spec/lib/gummi/repository_layer/repository_spec.rb +63 -0
- data/spec/lib/repobahn/repository_spec.rb +72 -0
- data/spec/spec_helper.rb +37 -9
- metadata +87 -37
- data/lib/gummi/default_index.rb +0 -13
- data/lib/gummi/document.rb +0 -139
- data/lib/gummi/document/attributes.rb +0 -28
- data/lib/gummi/document/object.rb +0 -12
- data/lib/gummi/document/search/filtered.rb +0 -39
- data/lib/gummi/document/search/raw.rb +0 -9
- data/lib/gummi/document/search/result.rb +0 -25
- data/lib/gummi/document/search/searching.rb +0 -45
- data/lib/gummi/entity.rb +0 -20
- data/lib/gummi/fields/boolean.rb +0 -10
- data/lib/gummi/fields/integer.rb +0 -14
- data/lib/gummi/fields/keyword.rb +0 -13
- data/lib/gummi/fields/ngram_and_plain.rb +0 -18
- data/lib/gummi/fields/path_hierarchy.rb +0 -13
- data/lib/gummi/fields/positive_integer.rb +0 -19
- data/lib/gummi/fields/sanitized_string.rb +0 -28
- data/lib/gummi/fields/string.rb +0 -15
- data/lib/gummi/fields/time.rb +0 -15
- data/lib/gummi/index.rb +0 -146
- data/lib/gummi/repository.rb +0 -38
- data/lib/gummi/repository/result.rb +0 -30
- data/spec/lib/gummi/document_spec.rb +0 -73
- data/spec/lib/gummi/repository/result_spec.rb +0 -18
- data/spec/lib/gummi/repository_spec.rb +0 -81
- data/spec/models/db/person.rb +0 -15
data/.gitignore
CHANGED
data/gummi.gemspec
CHANGED
@@ -17,12 +17,13 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency
|
21
|
-
spec.add_dependency
|
22
|
-
spec.add_dependency
|
23
|
-
spec.add_dependency
|
24
|
-
spec.add_dependency
|
25
|
-
spec.add_dependency
|
20
|
+
spec.add_dependency 'virtus', '~> 1.0.0'
|
21
|
+
spec.add_dependency 'elasticsearch', '~> 0.4.0'
|
22
|
+
spec.add_dependency 'activesupport', '>= 3.0'
|
23
|
+
spec.add_dependency 'activemodel', '>= 3.0'
|
24
|
+
spec.add_dependency 'hooks', '~>0.3.3'
|
25
|
+
spec.add_dependency 'leaflet'
|
26
|
+
spec.add_dependency 'hashie', '>= 2.0.0'
|
26
27
|
|
27
28
|
spec.add_development_dependency('bundler', '~> 1.3')
|
28
29
|
spec.add_development_dependency('pry')
|
data/lib/gummi.rb
CHANGED
@@ -4,40 +4,46 @@ require 'active_support/core_ext'
|
|
4
4
|
require 'active_model'
|
5
5
|
require 'hooks'
|
6
6
|
require 'leaflet'
|
7
|
+
require 'hashie/mash'
|
7
8
|
|
8
|
-
require
|
9
|
-
require
|
9
|
+
require 'repobahn/repository/active_record'
|
10
|
+
require 'repobahn/repository'
|
11
|
+
require 'repobahn/entity'
|
10
12
|
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
26
|
-
require
|
27
|
-
require
|
28
|
-
require
|
29
|
-
require
|
30
|
-
require
|
31
|
-
require
|
32
|
-
require
|
33
|
-
|
13
|
+
require 'gummi/version'
|
14
|
+
require 'gummi/errors'
|
15
|
+
require 'gummi/api'
|
16
|
+
|
17
|
+
require 'gummi/db_layer/index'
|
18
|
+
require 'gummi/db_layer/document/attributes'
|
19
|
+
require 'gummi/db_layer/document/object'
|
20
|
+
require 'gummi/db_layer/document'
|
21
|
+
require 'gummi/db_layer/fields/boolean'
|
22
|
+
require 'gummi/db_layer/fields/time'
|
23
|
+
require 'gummi/db_layer/fields/integer'
|
24
|
+
require 'gummi/db_layer/fields/positive_integer'
|
25
|
+
require 'gummi/db_layer/fields/keyword'
|
26
|
+
require 'gummi/db_layer/fields/ngram_and_plain'
|
27
|
+
require 'gummi/db_layer/fields/path_hierarchy'
|
28
|
+
require 'gummi/db_layer/fields/string'
|
29
|
+
require 'gummi/db_layer/fields/sanitized_string'
|
30
|
+
require 'gummi/db_layer/default_index'
|
31
|
+
require 'gummi/db_layer/document/search/searching'
|
32
|
+
require 'gummi/db_layer/document/search/filtered'
|
33
|
+
require 'gummi/db_layer/document/search/raw'
|
34
|
+
require 'gummi/db_layer/document/search/result'
|
35
|
+
|
36
|
+
require 'gummi/repository_layer/repository'
|
37
|
+
require 'gummi/repository_layer/repository/result'
|
38
|
+
|
39
|
+
require 'gummi/entity_layer/entity'
|
34
40
|
|
35
41
|
module Gummi
|
36
42
|
def self.env
|
37
43
|
if defined? Rails
|
38
44
|
Rails.env
|
39
45
|
else
|
40
|
-
RAILS_ENV ||
|
46
|
+
RAILS_ENV || 'development'
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
data/lib/gummi/api.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gummi
|
2
|
+
module DbLayer
|
3
|
+
class DefaultIndex
|
4
|
+
include Gummi::DbLayer::Index
|
5
|
+
|
6
|
+
def self.name
|
7
|
+
if defined? Rails
|
8
|
+
"#{Rails.application.class.name.deconstantize.underscore}_#{Rails.env}"
|
9
|
+
else
|
10
|
+
"gummi_#{Gummi.env}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module Gummi
|
2
|
+
module DbLayer
|
3
|
+
module Document
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include Virtus.model
|
8
|
+
include Gummi::DbLayer::Document::Attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
# –––––––––––––
|
12
|
+
# Class Methods
|
13
|
+
# –––––––––––––
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
# ––––––––––––––––––––––
|
18
|
+
# Public Persistence API
|
19
|
+
# ––––––––––––––––––––––
|
20
|
+
|
21
|
+
def create(*args)
|
22
|
+
instance = new(*args)
|
23
|
+
instance.create ? instance : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(*args)
|
27
|
+
get! *args
|
28
|
+
rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# –––––––––––––––––––––
|
33
|
+
# Public Conversion API
|
34
|
+
# –––––––––––––––––––––
|
35
|
+
|
36
|
+
def hits_to_documents(hits)
|
37
|
+
documents = [hits].flatten.map { |hit| hit_to_document(hit) }
|
38
|
+
documents.length > 1 ? documents : documents.first
|
39
|
+
end
|
40
|
+
|
41
|
+
def hit_to_document(hit)
|
42
|
+
attributes = { id: hit._id, version: hit._version }
|
43
|
+
attributes.merge!(parent_id_attribute_name => hit.fields._parent) if hit.fields && hit.fields._parent
|
44
|
+
attributes.merge! hit._source if hit._source
|
45
|
+
self.new attributes
|
46
|
+
end
|
47
|
+
|
48
|
+
# ––––––––––––––––––––––––––––––
|
49
|
+
# Index and Document definitions
|
50
|
+
# ––––––––––––––––––––––––––––––
|
51
|
+
|
52
|
+
def index(*args)
|
53
|
+
@index = args.first unless args.empty?
|
54
|
+
@index || Gummi::DbLayer::DefaultIndex
|
55
|
+
end
|
56
|
+
|
57
|
+
def document_type(*args)
|
58
|
+
@document_type = args.first.to_sym unless args.empty?
|
59
|
+
@document_type || name.demodulize.underscore.to_sym
|
60
|
+
end
|
61
|
+
|
62
|
+
def parent(model)
|
63
|
+
parent_document_type model.document_type
|
64
|
+
end
|
65
|
+
|
66
|
+
def parent_document_type(*args)
|
67
|
+
@parent_document_type = args.first unless args.empty?
|
68
|
+
parent_id_attribute_name "#{@parent_document_type}_id".to_sym if @parent_document_type && !parent_id_attribute_name
|
69
|
+
@parent_document_type
|
70
|
+
end
|
71
|
+
|
72
|
+
def parent_id_attribute_name(*args)
|
73
|
+
unless args.empty?
|
74
|
+
@parent_id_attribute_name = args.first
|
75
|
+
attr_accessor @parent_id_attribute_name
|
76
|
+
end
|
77
|
+
@parent_id_attribute_name
|
78
|
+
end
|
79
|
+
|
80
|
+
# ––––––––––––––––
|
81
|
+
# Public Index API
|
82
|
+
# ––––––––––––––––
|
83
|
+
|
84
|
+
def sync_mapping!
|
85
|
+
client.indices.put_mapping creation_options
|
86
|
+
end
|
87
|
+
|
88
|
+
def creation_options
|
89
|
+
result = {
|
90
|
+
index: index.name,
|
91
|
+
type: document_type,
|
92
|
+
body: {
|
93
|
+
document_type => {
|
94
|
+
properties: mapping,
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
result[:body][document_type].merge!(_parent: { type: parent_document_type }) if parent_document_type.present?
|
99
|
+
result
|
100
|
+
end
|
101
|
+
|
102
|
+
# –––––––––––––––––
|
103
|
+
# Public Search API
|
104
|
+
# –––––––––––––––––
|
105
|
+
|
106
|
+
def new_filtered_search(options = {})
|
107
|
+
Gummi::DbLayer::Document::Search::Filtered.new default_search_options.merge(options)
|
108
|
+
end
|
109
|
+
|
110
|
+
def new_raw_search(options = {})
|
111
|
+
Gummi::DbLayer::Document::Search::Raw.new default_search_options.merge(options)
|
112
|
+
end
|
113
|
+
|
114
|
+
def default_search_options
|
115
|
+
{
|
116
|
+
document_class: self,
|
117
|
+
index: index.name,
|
118
|
+
type: document_type,
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
# –––––––––––––––––––––––––––
|
123
|
+
# Internal Backend Connection
|
124
|
+
# –––––––––––––––––––––––––––
|
125
|
+
|
126
|
+
def client
|
127
|
+
Gummi::API.client
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# ––––––––––––––––––––––––
|
133
|
+
# Internal Persistence API
|
134
|
+
# ––––––––––––––––––––––––
|
135
|
+
|
136
|
+
def get!(id, parent_id = nil)
|
137
|
+
options = { index: index.name, type: document_type, id: id, fields: %w{ _source _parent } }
|
138
|
+
if parent_id
|
139
|
+
options.merge! parent: parent_id
|
140
|
+
elsif parent_document_type
|
141
|
+
raise ArgumentError, "The parent_id attribute is required for getting #{name} from Elastic Search"
|
142
|
+
end
|
143
|
+
response = Hashie::Mash.new client.get options
|
144
|
+
hit_to_document response
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
# ––––––––––––––––
|
150
|
+
# Instance Methods
|
151
|
+
# ––––––––––––––––
|
152
|
+
|
153
|
+
attr_accessor :id
|
154
|
+
attr_accessor :version
|
155
|
+
|
156
|
+
# ––––––––––––––––––––––
|
157
|
+
# Public Persistence API
|
158
|
+
# ––––––––––––––––––––––
|
159
|
+
|
160
|
+
def create(method = :create)
|
161
|
+
attributes = self.attributes
|
162
|
+
if parent_id_attribute_name && parent_id = self.send(parent_id_attribute_name)
|
163
|
+
options = { parent: parent_id }
|
164
|
+
else
|
165
|
+
options = {}
|
166
|
+
end
|
167
|
+
raise ArgumentError unless [:create, :index].include?(method)
|
168
|
+
response = Hashie::Mash.new client.send(method, options.merge(index: index.name, type: document_type, id: self.id, body: attributes))
|
169
|
+
if response.ok
|
170
|
+
self.id = response._id
|
171
|
+
self.version = response._version
|
172
|
+
true
|
173
|
+
else
|
174
|
+
false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def overwrite
|
179
|
+
create :index
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
# ––––––––––––––––––––
|
185
|
+
# Class method proxies
|
186
|
+
# ––––––––––––––––––––
|
187
|
+
|
188
|
+
def index
|
189
|
+
self.class.index
|
190
|
+
end
|
191
|
+
|
192
|
+
def document_type
|
193
|
+
self.class.document_type
|
194
|
+
end
|
195
|
+
|
196
|
+
def parent_id_attribute_name
|
197
|
+
self.class.parent_id_attribute_name
|
198
|
+
end
|
199
|
+
|
200
|
+
def client
|
201
|
+
self.class.client
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Gummi
|
2
|
+
module DbLayer
|
3
|
+
module Document
|
4
|
+
module Attributes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def mapping_for_attribute(attribute)
|
10
|
+
if attribute.is_a? Virtus::Attribute::EmbeddedValue
|
11
|
+
{ properties: attribute.primitive.mapping }
|
12
|
+
elsif attribute.is_a? Virtus::Attribute::Collection
|
13
|
+
mapping_for_attribute(attribute.member_type)
|
14
|
+
else
|
15
|
+
begin
|
16
|
+
attribute.mapping
|
17
|
+
rescue NoMethodError => exception
|
18
|
+
if exception.message.include?("`mapping'")
|
19
|
+
raise Errors::ImplicitMappingForbidden, "Sorry, you have to explicitly define the attribute type for #{attribute.instance_variable_name rescue nil} in #{self} or maybe you tried to use Array[] where it is not allowed. Original Exception: #{exception.message}"
|
20
|
+
else
|
21
|
+
raise exception
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def mapping
|
28
|
+
result = {}
|
29
|
+
attribute_set.each do |attribute|
|
30
|
+
result.merge! attribute.name => mapping_for_attribute(attribute)
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Gummi
|
2
|
+
module DbLayer
|
3
|
+
module Document
|
4
|
+
module Search
|
5
|
+
class Filtered
|
6
|
+
include Gummi::DbLayer::Document::Search::Searching
|
7
|
+
|
8
|
+
attribute :query_string, Gummi::DbLayer::Fields::SanitizedString
|
9
|
+
attribute :query_filters, Array[Hash], default: []
|
10
|
+
attribute :facets, Hash, default: {}
|
11
|
+
|
12
|
+
def to_client_args
|
13
|
+
args = super
|
14
|
+
args[:body] = { query: filtered, facets: facets }
|
15
|
+
args
|
16
|
+
end
|
17
|
+
|
18
|
+
def query
|
19
|
+
if query_string.present?
|
20
|
+
{query_string: { query: query_string}}
|
21
|
+
else
|
22
|
+
{match_all: {}}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def filtered
|
27
|
+
{ 'filtered' => { 'query' => query, 'filter' => process_query_filters }}
|
28
|
+
end
|
29
|
+
|
30
|
+
def process_query_filters
|
31
|
+
if query_filters.length > 1
|
32
|
+
{and: query_filters}
|
33
|
+
else
|
34
|
+
query_filters.first
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|