ansr_blacklight 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDk3NWUxNDFiNTdiYWY1MjU1Y2E3NzA4MWFmNTE4NDZkNzQ5NDg1MQ==
5
+ data.tar.gz: !binary |-
6
+ NjM0ZWI5NjhkMTIwNGIxYTJiMmQyNDVlMmVhM2I1ODNhMzYyYjE3YQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MDcxMjYzZTA1MzliMThmOWFhYzk4YTQ2MWRmODIxZmM3MWNlZWQxOTQ1NjZm
10
+ ZDU5N2ExODMxMTczZWE3NWFhMjg5YjNjOTI0ZWE5YjgwZDE0MGRkZGM3OTlk
11
+ OGM1ZGMzZjUxNjQ5MWNkN2Q1ODc2ZDNlNTY4M2U3YjcxNDU2Mjg=
12
+ data.tar.gz: !binary |-
13
+ M2MxNDgxMjE3YWIxMjc0MmY3NzBkYjg1YWMxMTMyZjUwNDIyZDAxMzA2NGY0
14
+ NWQwYzI0Y2MzYzY2MjU5ZmJmZWY4NzA4OWEwNjMzMDQ2MTU5ZWNmZTgwNDJi
15
+ NTA1OGZjYzE0NWY2YWUwY2NhZGIyNDk2MzYzMDIyY2M3MjIwMWE=
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ Ansr::Blacklight
2
+ =================
3
+
4
+ A re-implementation of Blacklight's Solr model with find/search functionality moved behind ActiveRecord::Relation subclasses.
5
+
6
+ QUESTIONS
7
+
8
+ Is a closer conformation to the expectations from ActiveRecord valuable enough to forego use of Sunspot (https://github.com/sunspot/sunspot)?
9
+
10
+ REQUEST REQUIREMENTS
11
+
12
+ Considering the following block from the BL Solr request code:
13
+ SINGULAR_KEYS = %W{ facet fl q qt rows start spellcheck spellcheck.q sort
14
+ per_page wt hl group defType}
15
+ ARRAY_KEYS = %W{facet.field facet.query facet.pivot fq hl.fl }
16
+
17
+ facet : a boolean field indicating the requested presence of facet info in response
18
+ fl : the selected fields
19
+ q : the query (fielding?)
20
+ qt : query type; indicates queryHandler in Solr
21
+ rows : corresponds to limit
22
+ start : corresponds to offset
23
+ spellcheck : boolean?
24
+ spellcheck.q : ?
25
+ sort : ?
26
+ facet.field : the fields for which facet info is requested
27
+ facet.query : ?
28
+ facet.pivot : ?
29
+ fq : ?
30
+ hl.fl : field to highlight
31
+ How is facet query different from filter query (fq)?
32
+
33
+ Relations must be configurable with default parameters; this is fairly easy to do with a template Relation to spawn the default scope from.
34
+
35
+ RESPONSE REQUIREMENTS
36
+
37
+ tbd
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/ansr/version')
2
+ version = Ansr.version
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'ansr_blacklight'
5
+ spec.version = version
6
+ spec.platform = Gem::Platform::RUBY
7
+ spec.authors = ["Benjamin Armintor"]
8
+ spec.email = ["armintor@gmail.com"]
9
+ spec.summary = 'ActiveRecord-style models and relations for Blacklight'
10
+ spec.description = 'Wrapping the Blacklight/RSolr in Rails-like models and relations'
11
+ spec.homepage = 'https://github.com/barmintor/ansr/tree/master/ansr_blacklight'
12
+ spec.files = `git ls-files`.split("\n")
13
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_dependency 'ansr', version
18
+ spec.add_dependency 'json-ld'
19
+ spec.add_dependency 'rest-client'
20
+ spec.add_dependency 'loggable'
21
+ spec.add_dependency "rails", ">= 3.2.6", "< 5"
22
+ # spec.add_dependency 'blacklight', '>=5.1.0'
23
+ spec.add_dependency 'sass-rails'
24
+ spec.add_development_dependency("rake")
25
+ spec.add_development_dependency("bundler", ">= 1.0.14")
26
+ spec.add_development_dependency "rspec-rails"
27
+ spec.add_development_dependency("yard")
28
+ end
@@ -0,0 +1,57 @@
1
+ require 'ansr'
2
+ require 'rsolr'
3
+ module Ansr::Blacklight
4
+ extend ActiveSupport::Autoload
5
+ autoload :SolrProjectionMethods, 'ansr_blacklight/relation/solr_projection_methods'
6
+ require 'ansr_blacklight/solr'
7
+ require 'ansr_blacklight/request_builders'
8
+ require 'ansr_blacklight/arel'
9
+ require 'ansr_blacklight/connection_adapters/no_sql_adapter'
10
+ require 'ansr_blacklight/relation'
11
+ require 'ansr_blacklight/model/querying'
12
+ require 'ansr_blacklight/base'
13
+
14
+ def self.solr_file
15
+ "#{::Rails.root.to_s}/config/solr.yml"
16
+ end
17
+
18
+ def self.solr
19
+ @solr ||= RSolr.connect(Ansr::Blacklight.solr_config)
20
+ end
21
+
22
+ def self.solr_config
23
+ @solr_config ||= begin
24
+ raise "The #{::Rails.env} environment settings were not found in the solr.yml config" unless solr_yml[::Rails.env]
25
+ solr_yml[::Rails.env].symbolize_keys
26
+ end
27
+ end
28
+
29
+ def self.solr_yml
30
+ require 'erb'
31
+ require 'yaml'
32
+
33
+ return @solr_yml if @solr_yml
34
+ unless File.exists?(solr_file)
35
+ raise "You are missing a solr configuration file: #{solr_file}. Have you run \"rails generate blacklight:install\"?"
36
+ end
37
+
38
+ begin
39
+ @solr_erb = ERB.new(IO.read(solr_file)).result(binding)
40
+ rescue Exception => e
41
+ raise("solr.yml was found, but could not be parsed with ERB. \n#{$!.inspect}")
42
+ end
43
+
44
+ begin
45
+ @solr_yml = YAML::load(@solr_erb)
46
+ rescue StandardError => e
47
+ raise("solr.yml was found, but could not be parsed.\n")
48
+ end
49
+
50
+ if @solr_yml.nil? || !@solr_yml.is_a?(Hash)
51
+ raise("solr.yml was found, but was blank or malformed.\n")
52
+ end
53
+
54
+ return @solr_yml
55
+ end
56
+
57
+ end
@@ -0,0 +1,6 @@
1
+ module Ansr::Blacklight
2
+ module Arel
3
+ require 'ansr_blacklight/arel/visitors'
4
+ require 'ansr_blacklight/arel/big_table'
5
+ end
6
+ end
@@ -0,0 +1,57 @@
1
+ module Ansr::Blacklight::Arel
2
+ class BigTable < Ansr::Arel::BigTable
3
+ attr_accessor :name
4
+
5
+ def initialize(klass, engine=nil, config=nil)
6
+ super(klass, engine)
7
+ @name = 'select'
8
+ self.config(config)
9
+ end
10
+
11
+ delegate :index_fields, to: :config
12
+ delegate :show_fields, to: :config
13
+ delegate :sort_fields, to: :config
14
+
15
+ def filterable
16
+ config.facet_fields.keys
17
+ end
18
+
19
+ alias_method :facets, :filterable
20
+
21
+ def filterable?(field)
22
+ filterable.include? field
23
+ end
24
+
25
+ def constrainable
26
+ index_fields.keys
27
+ end
28
+
29
+ def constrainable?(field)
30
+ index_fields.include?(field)
31
+ end
32
+
33
+ def selectable
34
+ show_fields.keys + index_fields.keys
35
+ end
36
+
37
+ def selectable?(field)
38
+ show_fields.include? field
39
+ end
40
+
41
+ def fields
42
+ (constrainable + selectable + filterable).uniq
43
+ end
44
+
45
+ def sortable
46
+ sort_fields.keys
47
+ end
48
+
49
+ def sortable?(field)
50
+ sort_fields.include? field
51
+ end
52
+
53
+ def primary_key
54
+ @primary_key ||= ::Arel::Attribute.new(self, config.document_unique_id_param.to_s)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,6 @@
1
+ module Ansr::Blacklight::Arel
2
+ module Visitors
3
+ require 'ansr_blacklight/arel/visitors/query_builder'
4
+ require 'ansr_blacklight/arel/visitors/to_no_sql'
5
+ end
6
+ end
@@ -0,0 +1,217 @@
1
+ module Ansr::Blacklight::Arel::Visitors
2
+ class QueryBuilder < Ansr::Arel::Visitors::QueryBuilder
3
+ include Ansr::Blacklight::RequestBuilders
4
+ attr_reader :solr_request
5
+
6
+ def initialize(table)
7
+ super(table)
8
+ @solr_request = Ansr::Blacklight::Solr::Request.new
9
+ table.configure_fields.each do |k,v|
10
+ unless v[:select].blank?
11
+ v[:select].each do |sk, sv|
12
+ key = "f.#{k}.#{sk}".to_sym
13
+ @solr_request[key] = sv
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ public
20
+ def query_opts
21
+ solr_request
22
+ end
23
+
24
+ # determines whether multiple values should accumulate or overwrite in merges
25
+ def multiple?(field_key)
26
+ true
27
+ end
28
+
29
+ def visit_String o, a
30
+ case a
31
+ when Ansr::Arel::Visitors::From
32
+ query_opts.path = o
33
+ when Ansr::Arel::Visitors::Filter
34
+ filter_field(o.to_sym)
35
+ when Ansr::Arel::Visitors::Order
36
+ order(o)
37
+ else
38
+ raise "visited String \"#{o}\" with #{a.to_s}"
39
+ end
40
+ end
41
+
42
+
43
+ def visit_Arel_Nodes_TableAlias(object, attribute)
44
+ solr_request[:qt] = object.name.to_s
45
+ opts = {qt: object.name.to_s}
46
+ if (cf = table[object.name]).is_a? Ansr::Arel::ConfiguredField
47
+ opts.merge!(cf.config.fetch(:query,{}))
48
+ end
49
+ solr_request.merge!(opts)
50
+ visit object.relation, attribute
51
+ end
52
+
53
+ def visit_Ansr_Arel_Nodes_ProjectionTraits(object, attribute)
54
+ solr_request[:wt] = object.wt if object.wt
55
+ solr_request[:defType] = object.defType if object.defType
56
+ visit(object.expr, attribute)
57
+ end
58
+
59
+ def visit_Arel_SqlLiteral(n, attribute)
60
+ select_val = n.to_s.split(" AS ")
61
+ if Ansr::Arel::Visitors::Filter === attribute
62
+ solr_request.append_facet_fields(select_val[0].to_sym)
63
+ else
64
+ field(select_val[0].to_sym)
65
+ if select_val[1]
66
+ query_opts.aliases ||= {}
67
+ query_opts.aliases[select_val[0]] = select_val[1]
68
+ end
69
+ end
70
+ end
71
+
72
+ def from(value)
73
+ if value.respond_to? :name
74
+ solr_request.path = value.name
75
+ else
76
+ solr_request.path = value.to_s
77
+ end
78
+ self.table=value if (value.is_a? Ansr::Arel::BigTable)
79
+ end
80
+
81
+ def field(field_name)
82
+ return unless field_name
83
+ old = query_opts[:fields] ? Array(query_opts[:fields]) : []
84
+ field_names = (old + Array(field_name)).uniq
85
+ if field_names[0]
86
+ query_opts[:fields] = field_names[1] ? field_names : field_names[0]
87
+ end
88
+ end
89
+
90
+ def filter_field(field_name)
91
+ return unless field_name
92
+ old = solr_request[:"facet.field"] ? Array(solr_request[:"facet.field"]) : []
93
+ fields = Array(field_name).delete_if {|x| old.include? x}
94
+ solr_request.append_facet_fields(fields)
95
+ end
96
+
97
+ def visit_Arel_Nodes_Equality(object, attribute)
98
+ field_key = (object.left.respond_to? :expr) ? field_key_from_node(object.left.expr) : field_key_from_node(object.left)
99
+ opts = {}
100
+ opts.merge!(local_field_params(field_key))
101
+ opts.merge!(object.left.config.fetch(:local,{})) if object.left.respond_to? :config
102
+ if Ansr::Arel::Visitors::Filter === attribute or Ansr::Arel::Nodes::Filter === object.left
103
+ add_filter_fq_to_solr(solr_request, f: {field_key => object.right}, opts: opts)
104
+ else
105
+ # check the table for configured fields
106
+ add_query_to_solr(field_key, object.right, opts)
107
+ end
108
+ end
109
+
110
+ def visit_Arel_Nodes_NotEqual(object, attribute)
111
+ end
112
+
113
+ def visit_Arel_Nodes_Or(object, attribute)
114
+ end
115
+
116
+ def visit_Arel_Nodes_Grouping(object, attribute)
117
+ visit object.expr, attribute
118
+ end
119
+
120
+ def visit_Arel_Nodes_Group(object, attribute)
121
+ solr_request[:group] = object.expr.to_s
122
+ end
123
+
124
+ def visit_Ansr_Arel_Nodes_Facet(object, attribute)
125
+ name = object.expr
126
+ name = name.name if name.respond_to? :name
127
+ default = false
128
+ if name == ::Arel.star
129
+ prefix = "facet."
130
+ default = true
131
+ else
132
+ filter_field(name.to_sym) unless default
133
+ solr_request.append_facet_fields(name.to_sym) unless default
134
+ prefix = "f.#{name}.facet."
135
+ end
136
+ # there's got to be a helper for this
137
+ if object.pivot
138
+ solr_request.append_facet_pivot with_ex_local_param(object.ex, object.pivot.join(","))
139
+ elsif object.query
140
+ solr_request.append_facet_query object.query.map { |k, x| with_ex_local_param(object.ex, x[:fq]) }
141
+ else
142
+ object.opts.each do |att, value|
143
+ solr_request["#{prefix}#{att.to_s}".to_sym] = value.to_s unless att == :ex
144
+ end
145
+ solr_request.append_facet_fields with_ex_local_param(object.ex, name.to_sym) unless default
146
+ end
147
+ end
148
+
149
+ def visit_Ansr_Arel_Nodes_Spellcheck(object, attribute)
150
+ unless object.expr == false
151
+ solr_request[:spellcheck] = object.expr.to_s
152
+ end
153
+ object.opts.each do |att, val|
154
+ solr_request["spellcheck.#{att.to_s}".to_sym] = val if att != :select
155
+ end
156
+ end
157
+
158
+ def visit_Ansr_Arel_Nodes_Highlight(object, attribute)
159
+ unless object.expr == false or object.expr == true
160
+ solr_request[:hl] = object.expr.to_s
161
+ end
162
+ object.opts.each do |att, val|
163
+ solr_request["hl.#{att.to_s}".to_sym] = val if att != :select
164
+ end
165
+ end
166
+
167
+ def order(*arel_nodes)
168
+ direction = nil
169
+ nodes = []
170
+ arel_nodes.inject(nodes) do |c, n|
171
+ if ::Arel::Nodes::Ordering === n
172
+ c << n
173
+ elsif n.is_a? String
174
+ _ns = n.split(',')
175
+ _ns.each do |_n|
176
+ _p = _n.split(/\s+/)
177
+ if (_p[1])
178
+ _p[1] = _p[1].downcase.to_sym
179
+ else
180
+ _p[1] = :asc
181
+ end
182
+ c << table[_p[0].to_sym].send(_p[1])
183
+ end
184
+ end
185
+ c
186
+ end
187
+ nodes.each do |node|
188
+ if ::Arel::Nodes::Ordering === node
189
+ if solr_request[:sort_by]
190
+ solr_request[:sort_by] = Array[solr_request[:sort_by]] << node.expr.name
191
+ else
192
+ solr_request[:sort_by] = node.expr.name
193
+ end
194
+ direction = :asc if (::Arel::Nodes::Ascending === node and direction)
195
+ direction = :desc if (::Arel::Nodes::Descending === node)
196
+ end
197
+ end
198
+ solr_request[:sort_order] = direction if direction
199
+ end
200
+
201
+ def visit_Arel_Nodes_Limit(object, attribute)
202
+ value = object.expr
203
+ if value and (value = value.to_i)
204
+ raise "Page size cannot be > 500 (#{value}" if value > 500
205
+ solr_request[:rows] = value.to_s
206
+ end
207
+ end
208
+
209
+ def visit_Arel_Nodes_Offset(object, attribute)
210
+ value = object.expr
211
+ if value
212
+ solr_request[:start] = value.to_s
213
+ end
214
+ end
215
+
216
+ end
217
+ end
@@ -0,0 +1,14 @@
1
+ module Ansr::Blacklight::Arel::Visitors
2
+ class ToNoSql < Ansr::Arel::Visitors::ToNoSql
3
+
4
+ def initialize(table)
5
+ super(table)
6
+ end
7
+
8
+ def query_builder()
9
+ Ansr::Blacklight::Arel::Visitors::QueryBuilder.new(table)
10
+ end
11
+
12
+ end
13
+
14
+ end