solrbee 0.1.2 → 0.5.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Makefile +1 -4
  4. data/README.md +1 -11
  5. data/config/api.yml +22 -0
  6. data/lib/rom/solr.rb +56 -0
  7. data/lib/rom/solr/array.rb +10 -0
  8. data/lib/rom/solr/commands.rb +17 -0
  9. data/lib/rom/solr/commands/create_documents.rb +15 -0
  10. data/lib/rom/solr/commands/delete_documents.rb +15 -0
  11. data/lib/rom/solr/commands/delete_documents_by_query.rb +15 -0
  12. data/lib/rom/solr/commands/update_documents.rb +15 -0
  13. data/lib/rom/solr/dataset.rb +68 -0
  14. data/lib/rom/solr/document_repo.rb +45 -0
  15. data/lib/rom/solr/documents_dataset.rb +62 -0
  16. data/lib/rom/solr/documents_paginator.rb +21 -0
  17. data/lib/rom/solr/gateway.rb +14 -0
  18. data/lib/rom/solr/query.rb +135 -0
  19. data/lib/rom/solr/query_builder.rb +36 -0
  20. data/lib/rom/solr/query_templates.rb +34 -0
  21. data/lib/rom/solr/relation.rb +39 -0
  22. data/lib/rom/solr/relations/documents_relation.rb +215 -0
  23. data/lib/rom/solr/relations/schema_info_relation.rb +78 -0
  24. data/lib/rom/solr/repository.rb +9 -0
  25. data/lib/rom/solr/request_handler.rb +52 -0
  26. data/lib/rom/solr/response_handler.rb +12 -0
  27. data/lib/rom/solr/schema.rb +7 -0
  28. data/lib/rom/solr/schema_info_dataset.rb +19 -0
  29. data/lib/rom/solr/schema_info_repo.rb +29 -0
  30. data/lib/rom/solr/utils.rb +36 -0
  31. data/lib/solrbee.rb +22 -28
  32. data/lib/solrbee/version.rb +1 -1
  33. data/solrbee.gemspec +3 -1
  34. data/test.sh +14 -0
  35. metadata +58 -9
  36. data/lib/solrbee/api_methods.rb +0 -94
  37. data/lib/solrbee/client.rb +0 -31
  38. data/lib/solrbee/cursor.rb +0 -37
  39. data/lib/solrbee/query.rb +0 -35
  40. data/lib/solrbee/request.rb +0 -45
  41. data/lib/solrbee/response.rb +0 -25
@@ -0,0 +1,36 @@
1
+ require 'rom/solr/query'
2
+
3
+ module ROM
4
+ module Solr
5
+ class QueryBuilder
6
+ include Utils
7
+
8
+ attr_accessor :queries
9
+
10
+ def initialize(queries = [])
11
+ @queries = queries
12
+ end
13
+
14
+ def to_a
15
+ queries
16
+ end
17
+
18
+ def raw(*queries)
19
+ self.queries += queries
20
+ end
21
+
22
+ def respond_to_missing?(name, include_private = false)
23
+ Query.public_method_defined?(name, false) || super
24
+ end
25
+
26
+ def method_missing(name, *args)
27
+ if Query.respond_to?(name, false)
28
+ self.queries += Query.send(name, *args)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ require 'delegate'
2
+
3
+ module ROM
4
+ module Solr
5
+ module QueryTemplates
6
+
7
+ #
8
+ # Simple template object with a `#render` method.
9
+ #
10
+ # N.B. Values must be properly escaped and/or
11
+ # formatted for Solr prior to rendering!
12
+ #
13
+ class Template < SimpleDelegator
14
+ # @param data [Hash]
15
+ def render(data)
16
+ __getobj__ % data
17
+ end
18
+ end
19
+
20
+ STANDARD = Template.new('%{field}:%{value}')
21
+
22
+ ABSENT = Template.new('-%{field}:[* TO *]')
23
+ BEFORE_DATE = Template.new('%{field}:[* TO %{value}]')
24
+ BEFORE_DAYS = Template.new('%{field}:[* TO NOW-%{value}DAYS]')
25
+ DISJUNCTION = Template.new('{!lucene q.op=OR df=%{field}}%{value}')
26
+ JOIN = Template.new('{!join from=%{from} to=%{to}}%{field}:%{value}')
27
+ NEGATION = Template.new('-%{field}:%{value}')
28
+ PRESENT = Template.new('%{field}:[* TO *]')
29
+ REGEXP = Template.new('%{field}:/%{value}/')
30
+ TERM = Template.new('{!term f=%{field}}%{value}')
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ require 'forwardable'
2
+
3
+ module ROM
4
+ module Solr
5
+ class Relation < ROM::HTTP::Relation
6
+ extend Forwardable
7
+
8
+ RESPONSE_WRITERS = %w[ csv geojson javabin json php phps python ruby smile velocity xlsx xml xslt ]
9
+
10
+ def_delegators :dataset, :response, :params
11
+
12
+ adapter :solr
13
+ auto_struct false
14
+ auto_map false
15
+
16
+ # Need this?
17
+ option :output_schema, default: ->{ NOOP_OUTPUT_SCHEMA }
18
+
19
+ def wt(writer)
20
+ add_params(wt: Types::Coercible::String.enum(*RESPONSE_WRITERS)[writer])
21
+ end
22
+
23
+ def log_params_list(*log_params)
24
+ new_log_params = Types::Array.of(Types::String)[log_params]
25
+
26
+ add_params(logParamsList: new_log_params.join(','))
27
+ end
28
+
29
+ def omit_header(omit = true)
30
+ add_params(omitHeader: Types::Bool[omit])
31
+ end
32
+
33
+ def count
34
+ to_enum.count
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,215 @@
1
+ require 'rom/solr/documents_paginator'
2
+ require 'rom/solr/query_builder'
3
+
4
+ module ROM
5
+ module Solr
6
+ class DocumentsRelation < Relation
7
+
8
+ def_delegators :dataset, :num_found, :num_found_exact?
9
+
10
+ schema(:documents) { }
11
+
12
+ # @override
13
+ def each(&block)
14
+ return super unless cursor?
15
+
16
+ return to_enum unless block_given?
17
+
18
+ DocumentsPaginator.new(dataset).each(&block)
19
+ end
20
+
21
+ # @override FIXME: Get from Solr schema unique key
22
+ def primary_key
23
+ :id
24
+ end
25
+
26
+ def by_unique_key(id)
27
+ q('%s:%s' % [ primary_key, id ])
28
+ end
29
+
30
+ def all
31
+ q('*:*')
32
+ end
33
+
34
+ # @override
35
+ def count
36
+ num_found_exact? ? num_found : super
37
+ end
38
+
39
+ # Set a cursor on the relation for pagination
40
+ def cursor
41
+ relation = add_params(cursorMark: '*')
42
+
43
+ # Sort must include a sort on unique key (id).
44
+ if /\bid\b/ =~ params[:sort]
45
+ relation
46
+ else
47
+ relation.sort(params[:sort], 'id ASC')
48
+ end
49
+ end
50
+
51
+ def cursor?
52
+ params.key?(:cursorMark)
53
+ end
54
+
55
+ def query(&block)
56
+ queries = QueryBuilder.new.instance_eval(&block).to_a
57
+
58
+ q(*queries)
59
+ end
60
+
61
+ def filter(&block)
62
+ queries = QueryBuilder.new.instance_eval(&block).to_a
63
+
64
+ fq(*queries)
65
+ end
66
+
67
+ #
68
+ # Commands
69
+ #
70
+
71
+ def insert(docs)
72
+ # FIXME: use input schema
73
+ data = Array.wrap(docs).map do |doc|
74
+ doc.transform_keys!(&:to_sym)
75
+ doc[:id] ||= UUID[]
76
+ doc
77
+ end
78
+
79
+ with_options(
80
+ base_path: 'update/json/docs',
81
+ content_type: 'application/json',
82
+ request_data: JSON.dump(data)
83
+ )
84
+ end
85
+
86
+ def update(docs)
87
+ # FIXME: use input schema
88
+ data = Array.wrap(docs)
89
+ .map { |doc| doc.transform_keys(&:to_sym) }
90
+ .select { |doc| doc.key?(:id) }
91
+
92
+ with_options(
93
+ base_path: 'update/json/docs',
94
+ content_type: 'application/json',
95
+ request_data: JSON.dump(data)
96
+ )
97
+ end
98
+
99
+ def delete(docs)
100
+ # FIXME: use input schema
101
+ ids = Array.wrap(docs)
102
+ .map { |doc| doc.transform_keys(&:to_sym) }
103
+ .map { |doc| doc.fetch(:id) }
104
+
105
+ with_options(
106
+ base_path: 'update',
107
+ content_type: 'application/json',
108
+ request_data: JSON.dump(delete: ids)
109
+ )
110
+ end
111
+
112
+ def delete_by_query(query)
113
+ with_options(
114
+ base_path: 'update',
115
+ content_type: 'application/json',
116
+ request_data: JSON.dump(delete: {query: query})
117
+ )
118
+ end
119
+
120
+ #
121
+ # Common Query Parameters
122
+ #
123
+
124
+ def q(*queries)
125
+ old_queries = Array.wrap(params[:q])
126
+ new_queries = Array.wrap(queries).flatten
127
+
128
+ add_params q: (old_queries + new_queries).join(' ')
129
+ end
130
+
131
+ def fq(*queries)
132
+ old_queries = Array.wrap(params[:fq])
133
+ new_queries = Array.wrap(queries).flatten
134
+
135
+ add_params fq: old_queries + new_queries
136
+ end
137
+
138
+ def fl(*fields)
139
+ new_fields = Types::Array.of(Types::Coercible::String)[fields]
140
+
141
+ add_params fl: new_fields.join(',')
142
+ end
143
+
144
+ alias_method :fields, :fl
145
+
146
+ def cache(enabled = true)
147
+ add_params cache: Types::Bool[enabled]
148
+ end
149
+
150
+ def segment_terminate_early(enabled = true)
151
+ add_params segmentTerminateEarly: Types::Bool[enabled]
152
+ end
153
+
154
+ def time_allowed(millis)
155
+ add_params timeAllowed: Types::Coercible::Integer[millis]
156
+ end
157
+
158
+ def explain_other(query)
159
+ add_params explainOther: Types::String[query]
160
+ end
161
+
162
+ def start(offset)
163
+ add_params start: Types::Coercible::Integer[offset]
164
+ end
165
+
166
+ def sort(*criteria)
167
+ new_sort = Types::Array.of(Types::String)[criteria]
168
+
169
+ add_params sort: new_sort.join(',')
170
+ end
171
+
172
+ def rows(num)
173
+ add_params rows: Types::Coercible::Integer[num]
174
+ end
175
+
176
+ alias_method :limit, :rows
177
+
178
+ def def_type(value)
179
+ add_params defType: Types::Coercible::String[value]
180
+ end
181
+
182
+ def debug(setting)
183
+ add_params debug: Types::Coercible::String.enum('query', 'timing', 'results', 'all', 'true')[setting]
184
+ end
185
+
186
+ def echo_params(setting)
187
+ add_params echoParams: Types::Coercible::String.enum('explicit', 'all', 'none')[setting]
188
+ end
189
+
190
+ def min_exact_count(num)
191
+ add_params minExactCount: Types::Coercible::Integer[num]
192
+ end
193
+
194
+ #
195
+ # Commit parameters
196
+ #
197
+ def commit(value = true)
198
+ add_params commit: Types::Bool[value]
199
+ end
200
+
201
+ def commit_within(millis)
202
+ add_params commitWithin: Types::Coercible::Integer.optional[millis]
203
+ end
204
+
205
+ def overwrite(value = true)
206
+ add_params overwrite: Types::Bool[value]
207
+ end
208
+
209
+ def expunge_deletes(value = true)
210
+ add_params expungeDeletes: Types::Bool[value]
211
+ end
212
+
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,78 @@
1
+ module ROM
2
+ module Solr
3
+ class SchemaInfoRelation < Relation
4
+
5
+ schema(:schema_info) do
6
+ # no-op
7
+ end
8
+
9
+ def show_defaults(show = true)
10
+ add_params(showDefaults: Types::Bool[show])
11
+ end
12
+
13
+ def include_dynamic(enabled = true)
14
+ add_params(includeDynamic: Types::Bool[enabled])
15
+ end
16
+
17
+ def info
18
+ self
19
+ end
20
+
21
+ def copy_fields(source_fields: nil, dest_fields: nil)
22
+ source_fl = Array.wrap(source_fields).join(',') unless source_fields.nil?
23
+ dest_fl = Array.wrap(dest_fields).join(',') unless dest_fields.nil?
24
+
25
+ with_path(:copyfields)
26
+ .add_params('source.fl'=>source_fl, 'dest.fl'=>dest_fl)
27
+ end
28
+
29
+ def dynamic_fields(defaults: true)
30
+ with_path(:dynamicfields)
31
+ .show_defaults(defaults)
32
+ end
33
+
34
+ def dynamic_field(name, defaults: true)
35
+ with_path("dynamicfields/#{name}")
36
+ .show_defaults(defaults)
37
+ end
38
+
39
+ def similarity
40
+ with_path :similarity
41
+ end
42
+
43
+ def unique_key
44
+ with_path :uniquekey
45
+ end
46
+
47
+ def version
48
+ with_path :version
49
+ end
50
+
51
+ def schema_name
52
+ with_path :name
53
+ end
54
+
55
+ def fields(dynamic: true, defaults: true)
56
+ with_path(:fields)
57
+ .include_dynamic(dynamic)
58
+ .show_defaults(defaults)
59
+ end
60
+
61
+ def field(name, defaults: true)
62
+ with_path("fields/#{name}")
63
+ .show_defaults(defaults)
64
+ end
65
+
66
+ def field_types(defaults: true)
67
+ with_path(:fieldtypes)
68
+ .show_defaults(defaults)
69
+ end
70
+
71
+ def field_type(name, defaults: true)
72
+ with_path("fieldtypes/#{name}")
73
+ .show_defaults(defaults)
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ module ROM
2
+ module Solr
3
+ class Repository < ROM::Repository
4
+
5
+ auto_struct false
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,52 @@
1
+ module ROM
2
+ module Solr
3
+ class RequestHandler
4
+
5
+ def self.call(dataset)
6
+ new(dataset).execute
7
+ end
8
+
9
+ attr_reader :dataset
10
+
11
+ def initialize(dataset)
12
+ @dataset = dataset
13
+ end
14
+
15
+ def execute
16
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme.eql?('https')) do |http|
17
+ http.request(request)
18
+ end
19
+ end
20
+
21
+ def request
22
+ request_class.new(uri.request_uri, headers).tap do |req|
23
+ if dataset.request_data?
24
+ req.body = dataset.request_data
25
+ req.content_type = dataset.content_type
26
+ end
27
+ end
28
+ end
29
+
30
+ def headers
31
+ dataset.headers.transform_keys(&:to_s)
32
+ end
33
+
34
+ def uri
35
+ @uri ||= URI(dataset.uri).tap do |u|
36
+ if dataset.params?
37
+ u.query ||= URI.encode_www_form(dataset.params)
38
+ end
39
+ end
40
+ end
41
+
42
+ def request_class
43
+ if dataset.request_data?
44
+ Net::HTTP::Post
45
+ else
46
+ Net::HTTP::Get
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end