solrbee 0.1.2 → 0.5.0

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