ansr_blacklight 0.0.3

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.
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