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.
Files changed (79) hide show
  1. data/.gitignore +1 -0
  2. data/gummi.gemspec +7 -6
  3. data/lib/gummi.rb +32 -26
  4. data/lib/gummi/api.rb +3 -1
  5. data/lib/gummi/db_layer/default_index.rb +15 -0
  6. data/lib/gummi/db_layer/document.rb +206 -0
  7. data/lib/gummi/db_layer/document/attributes.rb +40 -0
  8. data/lib/gummi/db_layer/document/object.rb +15 -0
  9. data/lib/gummi/db_layer/document/search/filtered.rb +42 -0
  10. data/lib/gummi/db_layer/document/search/raw.rb +12 -0
  11. data/lib/gummi/db_layer/document/search/result.rb +34 -0
  12. data/lib/gummi/db_layer/document/search/searching.rb +51 -0
  13. data/lib/gummi/db_layer/fields/boolean.rb +13 -0
  14. data/lib/gummi/db_layer/fields/integer.rb +16 -0
  15. data/lib/gummi/db_layer/fields/keyword.rb +15 -0
  16. data/lib/gummi/db_layer/fields/ngram_and_plain.rb +20 -0
  17. data/lib/gummi/db_layer/fields/path_hierarchy.rb +15 -0
  18. data/lib/gummi/db_layer/fields/positive_integer.rb +21 -0
  19. data/lib/gummi/db_layer/fields/sanitized_string.rb +30 -0
  20. data/lib/gummi/db_layer/fields/string.rb +17 -0
  21. data/lib/gummi/db_layer/fields/time.rb +17 -0
  22. data/lib/gummi/db_layer/index.rb +150 -0
  23. data/lib/gummi/entity_layer/entity.rb +22 -0
  24. data/lib/gummi/errors.rb +7 -0
  25. data/lib/gummi/repository_layer/repository.rb +39 -0
  26. data/lib/gummi/repository_layer/repository/result.rb +42 -0
  27. data/lib/gummi/version.rb +1 -1
  28. data/lib/repobahn/repository.rb +25 -33
  29. data/lib/repobahn/repository/active_record.rb +17 -0
  30. data/spec/fixtures/admin/auto.rb +6 -0
  31. data/spec/fixtures/admin/cars.rb +12 -0
  32. data/spec/fixtures/admin/countries.rb +9 -0
  33. data/spec/fixtures/admin/country.rb +6 -0
  34. data/spec/fixtures/admin/db/country.rb +7 -0
  35. data/spec/fixtures/admin/db/vehicle.rb +7 -0
  36. data/spec/fixtures/cities.rb +7 -0
  37. data/spec/fixtures/city.rb +6 -0
  38. data/spec/fixtures/db/animal.rb +9 -0
  39. data/spec/fixtures/db/boat.rb +9 -0
  40. data/spec/fixtures/db/car.rb +9 -0
  41. data/spec/fixtures/db/city.rb +8 -0
  42. data/spec/fixtures/db/enemy.rb +10 -0
  43. data/spec/fixtures/db/game.rb +7 -0
  44. data/spec/fixtures/db/person.rb +15 -0
  45. data/spec/fixtures/db/rating.rb +11 -0
  46. data/spec/fixtures/db/ship.rb +18 -0
  47. data/spec/{models → fixtures}/people.rb +6 -2
  48. data/spec/{models → fixtures}/person.rb +3 -2
  49. data/spec/lib/gummi/db_layer/document_spec.rb +124 -0
  50. data/spec/lib/gummi/{entity_spec.rb → entity_layer/entity_spec.rb} +3 -1
  51. data/spec/lib/gummi/repository_layer/repository_spec.rb +63 -0
  52. data/spec/lib/repobahn/repository_spec.rb +72 -0
  53. data/spec/spec_helper.rb +37 -9
  54. metadata +87 -37
  55. data/lib/gummi/default_index.rb +0 -13
  56. data/lib/gummi/document.rb +0 -139
  57. data/lib/gummi/document/attributes.rb +0 -28
  58. data/lib/gummi/document/object.rb +0 -12
  59. data/lib/gummi/document/search/filtered.rb +0 -39
  60. data/lib/gummi/document/search/raw.rb +0 -9
  61. data/lib/gummi/document/search/result.rb +0 -25
  62. data/lib/gummi/document/search/searching.rb +0 -45
  63. data/lib/gummi/entity.rb +0 -20
  64. data/lib/gummi/fields/boolean.rb +0 -10
  65. data/lib/gummi/fields/integer.rb +0 -14
  66. data/lib/gummi/fields/keyword.rb +0 -13
  67. data/lib/gummi/fields/ngram_and_plain.rb +0 -18
  68. data/lib/gummi/fields/path_hierarchy.rb +0 -13
  69. data/lib/gummi/fields/positive_integer.rb +0 -19
  70. data/lib/gummi/fields/sanitized_string.rb +0 -28
  71. data/lib/gummi/fields/string.rb +0 -15
  72. data/lib/gummi/fields/time.rb +0 -15
  73. data/lib/gummi/index.rb +0 -146
  74. data/lib/gummi/repository.rb +0 -38
  75. data/lib/gummi/repository/result.rb +0 -30
  76. data/spec/lib/gummi/document_spec.rb +0 -73
  77. data/spec/lib/gummi/repository/result_spec.rb +0 -18
  78. data/spec/lib/gummi/repository_spec.rb +0 -81
  79. data/spec/models/db/person.rb +0 -15
@@ -1,28 +0,0 @@
1
- module Gummi
2
- module Document
3
- module Attributes
4
- extend ActiveSupport::Concern
5
-
6
- module ClassMethods
7
-
8
- def mapping_for_attribute(attribute)
9
- if attribute.is_a? Virtus::Attribute::EmbeddedValue
10
- {properties: attribute.primitive.mapping}
11
- elsif attribute.is_a? Virtus::Attribute::Collection
12
- mapping_for_attribute(attribute.member_type)
13
- else
14
- attribute.mapping
15
- end
16
- end
17
-
18
- def mapping
19
- result = {}
20
- attribute_set.each do |attribute|
21
- result.merge!({ attribute.name => mapping_for_attribute(attribute)})
22
- end
23
- result
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,12 +0,0 @@
1
- module Gummi
2
- module Document
3
- module Object
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- include Virtus.model
8
- include Gummi::Document::Attributes
9
- end
10
- end
11
- end
12
- end
@@ -1,39 +0,0 @@
1
- module Gummi
2
- module Document
3
- module Search
4
- class Filtered
5
- include Gummi::Document::Search::Searching
6
-
7
- attribute :query_string, Gummi::Fields::SanitizedString
8
- attribute :query_filters, Array[Hash], default: []
9
- attribute :facets, Hash, default: {}
10
-
11
- def to_client_args
12
- args = super
13
- args[:body] = {query: filtered, facets: facets }
14
- args
15
- end
16
-
17
- def query
18
- if query_string.present?
19
- {query_string: { query: query_string}}
20
- else
21
- {match_all: {}}
22
- end
23
- end
24
-
25
- def filtered
26
- { 'filtered' => { 'query' => query, 'filter' => process_query_filters }}
27
- end
28
-
29
- def process_query_filters
30
- if query_filters.length > 1
31
- {and: query_filters}
32
- else
33
- query_filters.first
34
- end
35
- end
36
- end
37
- end
38
- end
39
- end
@@ -1,9 +0,0 @@
1
- module Gummi
2
- module Document
3
- module Search
4
- class Raw
5
- include Gummi::Document::Search::Searching
6
- end
7
- end
8
- end
9
- end
@@ -1,25 +0,0 @@
1
- module Gummi
2
- module Document
3
- module Search
4
- class Result
5
-
6
- attr_reader :took, :total, :hits, :facets
7
-
8
- def initialize(result)
9
- @took = result["took"]
10
- @total = result["hits"]["total"]
11
- @hits = result["hits"]["hits"]
12
- @facets = result["facets"]
13
- end
14
-
15
- def records
16
- hits.map do |hit|
17
- model = "DB::#{hit["_type"].humanize}".constantize
18
- doc_hash = {id: hit["_id"]}.merge(hit["_source"])
19
- model.new(doc_hash)
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,45 +0,0 @@
1
- module Gummi
2
- module Document
3
- module Search
4
- module Searching
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- include Virtus.model
9
-
10
- attribute :type, String
11
- attribute :index, String, default: lambda {|search, attr| Gummi::DefaultIndex.name}
12
- attribute :page, Gummi::Fields::PositiveInteger, default: 1
13
- attribute :per_page, Gummi::Fields::PositiveInteger, default: 300
14
- attribute :options, Hash, default: {}
15
- end
16
-
17
- def size
18
- per_page
19
- end
20
-
21
- def from
22
- per_page * (page - 1)
23
- end
24
-
25
- def execute
26
- Gummi::Document::Search::Result.new client.search(to_client_args)
27
- end
28
-
29
- def to_client_args
30
- args = {}
31
- args[:index] = index
32
- args[:type] = type if type
33
- args[:from] = from
34
- args[:size] = size
35
- args.merge options
36
- end
37
-
38
- private
39
- def client
40
- Gummi::API.client
41
- end
42
- end
43
- end
44
- end
45
- end
data/lib/gummi/entity.rb DELETED
@@ -1,20 +0,0 @@
1
- module Gummi
2
- module Entity
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- include Repobahn::Entity
7
- end
8
-
9
- attr_accessor :id
10
- attr_accessor :version
11
-
12
- def ==(other)
13
- other &&
14
- self.id == other.id &&
15
- self.version == other.version &&
16
- self.attributes == other.attributes
17
- end
18
-
19
- end
20
- end
@@ -1,10 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class Boolean < Virtus::Attribute::Boolean
4
-
5
- def mapping
6
- { type: 'boolean' }
7
- end
8
- end
9
- end
10
- end
@@ -1,14 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class Integer < Virtus::Attribute
4
-
5
- def coerce(value)
6
- value.to_i if value.present?
7
- end
8
-
9
- def mapping
10
- { type: 'integer' }
11
- end
12
- end
13
- end
14
- end
@@ -1,13 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class Keyword < Virtus::Attribute
4
- def coerce(value)
5
- value
6
- end
7
-
8
- def mapping
9
- { type: 'string', index_analyzer: 'keyword_index_analyzer', search_analyzer: 'keyword_search_analyzer' }
10
- end
11
- end
12
- end
13
- end
@@ -1,18 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class NgramAndPlain < Virtus::Attribute
4
- def coerce(value)
5
- value
6
- end
7
-
8
- def mapping
9
- { type: 'multi_field',
10
- fields: {
11
- name => { type: 'string', index_analyzer: 'text_index_analyzer', search_analyzer: 'text_search_analyzer' },
12
- :plain => { type: 'string', index_analyzer: 'string_index_analyzer', search_analyzer: 'text_search_analyzer' },
13
- }
14
- }
15
- end
16
- end
17
- end
18
- end
@@ -1,13 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class PathHierarchy < Virtus::Attribute
4
- def coerce(value)
5
- value
6
- end
7
-
8
- def mapping
9
- {type: 'string', index_analyzer: 'path_hierarchy_analyzer' }
10
- end
11
- end
12
- end
13
- end
@@ -1,19 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class PositiveInteger < Virtus::Attribute
4
-
5
- def coerce(value)
6
- coerced = value.to_i
7
- if coerced > 0
8
- coerced
9
- else
10
- default_value.value
11
- end
12
- end
13
-
14
- def mapping
15
- { type: 'integer' }
16
- end
17
- end
18
- end
19
- end
@@ -1,28 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class SanitizedString < Virtus::Attribute
4
-
5
- def coerce(value)
6
- return nil if value.blank?
7
- sanitize_string_for_query(value.to_s)
8
- end
9
-
10
- def mapping
11
- { type: 'string' }
12
- end
13
-
14
- def sanitize_string_for_query(str)
15
- # Escape special characters
16
- escaped_characters = Regexp.escape('\/\\+-&|!(){}[]^~*?:')
17
- str = str.gsub(/([#{escaped_characters}])/) do |match|
18
- '\\'+match
19
- end
20
-
21
- # Escape odd quotes
22
- quote_count = str.count '"'
23
- str = str.gsub(/(.*)"(.*)/, '\1\"\3') if quote_count % 2 == 1
24
- str
25
- end
26
- end
27
- end
28
- end
@@ -1,15 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class String < Virtus::Attribute
4
-
5
-
6
- def coerce(value)
7
- value
8
- end
9
-
10
- def mapping
11
- { type: 'string' }
12
- end
13
- end
14
- end
15
- end
@@ -1,15 +0,0 @@
1
- module Gummi
2
- module Fields
3
- class Time < Virtus::Attribute
4
-
5
- def coerce(value)
6
- return nil unless value.respond_to? :in_time_zone
7
- value.in_time_zone 'UTC'
8
- end
9
-
10
- def mapping
11
- {type: "date"}
12
- end
13
- end
14
- end
15
- end
data/lib/gummi/index.rb DELETED
@@ -1,146 +0,0 @@
1
- module Gummi
2
- module Index
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
6
-
7
- # Return true if created or false if already created.
8
- #
9
- def setup
10
- created_settings = client.indices.create index: name, body: { settings: settings }
11
- created_settings.present?
12
- refresh
13
- rescue ::Elasticsearch::Transport::Transport::Errors::BadRequest => exception
14
- false
15
- end
16
-
17
- # Return true if successful or already teared down.
18
- #
19
- # Raises NotImplementedError in production.
20
- #
21
- def teardown
22
- raise NotImplementedError if Gummi.env == 'production'
23
- response = client.indices.delete index: name
24
- response.present?
25
- rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
26
- true
27
- end
28
-
29
- def name
30
- raise "Implement me"
31
- end
32
-
33
- def refresh
34
- client.indices.refresh
35
- client.cluster.health wait_for_status: :yellow
36
- end
37
-
38
- def settings
39
- default_settings
40
- end
41
-
42
- def default_settings
43
- {
44
- index: {
45
- # Main Settings
46
- number_of_shards: '3',
47
- number_of_replicas: (Gummi.env == 'production' ? '2' : '0'),
48
- refresh_interval: '1s',
49
- store: { type: (Gummi.env == 'test' ? :memory : :niofs) },
50
- mapper: { dynamic: false },
51
-
52
- analysis: {
53
-
54
- # Tokenizers are just some sort of "tool" or "module" that can be applied to analyzers.
55
- tokenizer: {
56
- # This one is a little bit more general and is able to chop any word into all of its components.
57
- ngram_tokenizer: {
58
- type: 'nGram',
59
- min_gram: 1,
60
- max_gram: 7,
61
- token_chars: [ 'letter', 'digit' ],
62
- }
63
-
64
- },
65
-
66
- # Now we are ready to use our tokenizers.
67
- # Let's create the most important thing: Analyzers.
68
- analyzer: {
69
-
70
- path_hierarchy_analyzer: {
71
- type: 'custom',
72
- tokenizer: 'path_hierarchy',
73
- },
74
- # When adding long text to Elastic, we most likely are going to use this
75
- # analyzer. This is commonly used for titles and descriptions.
76
- text_index_analyzer: {
77
- type: 'custom',
78
- tokenizer: 'ngram_tokenizer', # Chopping every word up into tokens
79
- filter: {
80
- 0 => 'standard', # Some default transformations
81
- 1 => 'lowercase', # Make everything lowercase
82
- 2 => 'word_delimiter', # E.g. "O'Neil" -> "O Neil", "Victoria's" -> "Victoria"
83
- 2 => 'asciifolding', # Transform everything into ASCII
84
- },
85
- },
86
-
87
- # For smaller texts, such as the city "stockholm", we don't want any
88
- # tokenizing. It's enough to explicitly save the word as it is.
89
- # As a matter of fact, if we would tokenize the city, then the facets
90
- # would report that we have Transports in "st" "sto" "stoc" etc.
91
- string_index_analyzer: {
92
- type: 'custom',
93
- tokenizer: 'standard',
94
- filter: {
95
- # The filters, however, are identical to the other analyzer.
96
- 0 => 'standard',
97
- 1 => 'lowercase',
98
- 2 => 'word_delimiter',
99
- 3 => 'asciifolding',
100
- },
101
- },
102
-
103
- # For finding Slugs
104
- keyword_index_analyzer: {
105
- type: 'custom',
106
- tokenizer: 'keyword',
107
- filter: {
108
- 0 => 'lowercase',
109
- 1 => 'asciifolding',
110
- },
111
- },
112
-
113
- # This is an analyzer that we apply to the search query itself.
114
- text_search_analyzer: {
115
- type: 'custom',
116
- tokenizer: 'standard',
117
- filter: {
118
- 0 => 'standard',
119
- 1 => 'lowercase',
120
- 2 => 'word_delimiter',
121
- 3 => 'asciifolding',
122
- },
123
- },
124
-
125
- # This is an analyzer that we apply to the search query itself.
126
- keyword_search_analyzer: {
127
- type: 'custom',
128
- tokenizer: 'keyword',
129
- filter: {
130
- 0 => 'lowercase',
131
- 1 => 'asciifolding',
132
- },
133
- },
134
-
135
- }
136
- }
137
- }
138
- }
139
- end
140
-
141
- def client
142
- Gummi::API.client
143
- end
144
- end
145
- end
146
- end