Skip to content

Commit 5a0698b

Browse files
authored
Merge pull request #6621 from avalonmediasystem/faster_unique
Use a solr query tailored for speed instead of Model.where.first when validating uniqueness
2 parents 8f42adc + 28198d2 commit 5a0698b

2 files changed

Lines changed: 22 additions & 44 deletions

File tree

app/validators/uniqueness_validator.rb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@ def initialize(options)
2222
end
2323
def validate_each(record, attribute, value)
2424
klass = record.class
25-
# existing_doc = find_doc(klass, value)
26-
existing_doc = find_doc(klass, record.to_solr[@solr_field])
27-
if ! existing_doc.nil? && existing_doc.id != record.id
25+
solr_value = record.to_solr[@solr_field]
26+
query = "has_model_ssim:\"#{klass.name}\""
27+
query += " AND #{@solr_field}:#{solr_value}" if solr_value.present?
28+
existing_doc_id = ActiveFedora::SolrService.query(query, fl: [:id], rows: 1).first&.dig('id')
29+
if existing_doc_id.present? && existing_doc_id != record.id
2830
record.errors.add(attribute, :taken, value: value)
2931
end
3032
end
31-
def find_doc(klass, value)
32-
klass.where(@solr_field => value).first
33-
end
3433
end

spec/validators/uniqueness_validator_spec.rb

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,40 @@
1919
before(:all) do
2020
class Foo < ActiveFedora::Base
2121
property :title, predicate: ::RDF::Vocab::DC.title, multiple: false
22+
23+
def to_solr
24+
super.tap do |solr_doc|
25+
solr_doc["title_uniq_si"] = title.downcase.gsub(/\s+/,'') if title.present?
26+
end
27+
end
2228
end
2329
end
2430
after(:all) { Object.send(:remove_const, :Foo) }
2531

26-
let(:solr_field) {"title_uniq_si"}
27-
let(:validator) {UniquenessValidator.new({:attributes => [:title], :solr_name => solr_field})}
28-
let(:record) {Foo.new}
32+
let(:field) { 'title' }
33+
let(:solr_field) { "title_uniq_si" }
34+
let(:validator) { UniquenessValidator.new({ :attributes => [field], :solr_name => solr_field }) }
35+
let(:record) { Foo.new(title: title) }
36+
let(:title) { 'new_title' }
2937

3038
it "should raise an exception if solr_name option is missing" do
31-
expect{UniquenessValidator.new({attributes: [:title]})}.to raise_error ArgumentError
39+
expect { UniquenessValidator.new({ attributes: [:title] }) }.to raise_error ArgumentError
3240
end
3341

3442
it "should not return errors when field is unique" do
35-
allow(validator).to receive("find_doc").and_return(nil)
3643
expect(record).not_to receive('errors')
37-
validator.validate_each(record, "title", "new_title")
44+
validator.validate_each(record, field, record.attributes[field])
3845
end
3946

4047
it "should not return errors when field is unique but record is the same" do
41-
doc = double(id: record.id)
42-
allow(validator).to receive("find_doc").and_return(doc)
48+
record.save!
4349
expect(record.errors).not_to receive('add')
44-
validator.validate_each(record, "title", "new_title")
50+
validator.validate_each(record, field, record.attributes[field])
4551
end
4652

4753
it "should return errors when field is not unique" do
48-
doc = double(id: 'different-id')
49-
allow(validator).to receive("find_doc").and_return(doc)
50-
# expect(record.errors).to receive('add')
51-
validator.validate_each(record, "title", "old_title")
54+
Foo.create(id: 'different-id', title: 'new_title')
55+
validator.validate_each(record, field, record.attributes[field])
5256
expect(record.errors).to_not be_empty
5357
end
54-
55-
describe "#find_doc" do
56-
let (:klass) {record.class}
57-
let (:value) {"old_title"}
58-
59-
it "should use the solr field name and supplied values" do
60-
relation = double()
61-
allow(relation).to receive("first")
62-
expect(klass).to receive(:where).once.with(solr_field => value).and_return(relation)
63-
validator.find_doc(klass, value)
64-
end
65-
it "should return one record when present" do
66-
doc = Foo.new
67-
relation = double()
68-
allow(relation).to receive("first").and_return(doc)
69-
allow(klass).to receive("where").and_return(relation)
70-
expect(validator.find_doc(klass, value)).to be_an_instance_of klass
71-
end
72-
it "should return nil when not present" do
73-
relation = double()
74-
allow(relation).to receive("first").and_return(nil)
75-
allow(klass).to receive("where").and_return(relation)
76-
expect(validator.find_doc(klass, value)).to be_nil
77-
end
78-
end
7958
end

0 commit comments

Comments
 (0)