gummi 0.1.2 → 0.2.0
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.
- 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
|