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 +15 -0
- data/Gemfile +3 -0
- data/README.md +37 -0
- data/ansr_blacklight.gemspec +28 -0
- data/lib/ansr_blacklight.rb +57 -0
- data/lib/ansr_blacklight/arel.rb +6 -0
- data/lib/ansr_blacklight/arel/big_table.rb +57 -0
- data/lib/ansr_blacklight/arel/visitors.rb +6 -0
- data/lib/ansr_blacklight/arel/visitors/query_builder.rb +217 -0
- data/lib/ansr_blacklight/arel/visitors/to_no_sql.rb +14 -0
- data/lib/ansr_blacklight/base.rb +21 -0
- data/lib/ansr_blacklight/connection_adapters/no_sql_adapter.rb +38 -0
- data/lib/ansr_blacklight/model/querying.rb +29 -0
- data/lib/ansr_blacklight/relation.rb +50 -0
- data/lib/ansr_blacklight/relation/solr_projection_methods.rb +55 -0
- data/lib/ansr_blacklight/request_builders.rb +141 -0
- data/lib/ansr_blacklight/solr.rb +4 -0
- data/lib/ansr_blacklight/solr/request.rb +46 -0
- data/lib/ansr_blacklight/solr/response.rb +94 -0
- data/lib/ansr_blacklight/solr/response/group.rb +32 -0
- data/lib/ansr_blacklight/solr/response/group_response.rb +50 -0
- data/lib/ansr_blacklight/solr/response/more_like_this.rb +14 -0
- data/lib/ansr_blacklight/solr/response/pagination_methods.rb +35 -0
- data/lib/ansr_blacklight/solr/response/spelling.rb +92 -0
- data/spec/fixtures/config.yml +0 -0
- data/spec/lib/loaded_relation_spec.rb +223 -0
- data/spec/lib/queryable_relation_spec.rb +133 -0
- data/spec/lib/relation/faceting_spec.rb +475 -0
- data/spec/lib/relation/grouping_spec.rb +159 -0
- data/spec/spec_helper.rb +72 -0
- metadata +225 -0
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
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,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,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
|