More efficient, rails way to check for any of three fields being unique?

Viewed 84

So, I need check three fields for uniqueness of an object before creating it (from a form), but I will create the object so long as any of the three fields are unique.

My first thought was to just pass the params from the controller to the model, and then run a query to check if a query with those three fields returns > 0 documents. However, I've since learned that this is a dangerous approach, and should not be used.

So I checked the docs, and based off of this snippet

Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once per semester for a particular class.

class TeacherSchedule < ActiveRecord::Base
  validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
end

I thought I had found my answer, and implemented:

validates_uniqueness_of :link_to_event, :scope => [:name_of_event, :date_of_event]

which works! But, this dataset is going to get very large (not from this form alone, lol), and I'm under the impression that with this implementation, Rails is going to query for all fields with a link_to_event, and then all fields with a name_of_event, and then all fields with a date_of_event. So, my question(s) is:

A) Am I wrong about how rails will implement this? Is it going to be more efficient out of the box?

B) If this will not be efficient for a table with a couple million entries, is there a better (and still railsy) way to do this?

2 Answers

You can define a method that queries the records with all the fields that you want to be unique as a group:

validate :uniqueness_of_teacher_semester_and_class



def uniqueness_of_teacher_semester_and_class
  users = self.class.where(teacher_id: teacher_id, semester_id: semester_id, class_id: class_id)

  errors.add :base, 'Record not unique.' if users.exists?
end

To answer your questions:

A) Am I wrong about how rails will implement this? Is it going to be more efficient out of the box?

I think Rails will query for a match on all 3 fields, and you should check the Mongo (or Rails) log to see for sure.

B) If this will not be efficient for a table with a couple million entries, is there a better (and still railsy) way to do this?

This is the Rails way. There are 2 things you can do to make it efficient:

  1. You would need indexes on all 3 fields, or a compound index of the 3 fields. The compound index *might* be faster, but you can benchmark to find out.

  2. You can add a new field with the 3 fields concatenated, and an index on it. But this will take up extra space and may not be faster than the compound index.

These days a couple million documents is not that much, but depends on document size and hardware.

Related