gummi 0.1.2 → 0.2.0

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