Skip to content

Validation and Case Sensitivity in Neo4j.rb 3.0

Brian Underwood edited this page Jun 28, 2016 · 1 revision

ActiveNode and ActiveRel's validations are provided by ActiveModel::Validations, so everything here or in your version of Rails should apply. There is one caveat when it comes to uniqueness and case-sensitivity, which has somewhat unique handling in Neo4j.

In Neo4j.rb, the preferred way of declaring a property unique is through a constraint, which is a type of database index that forces uniqueness. As described here, the proper syntax for this:

property :name, constraint: :unique

The reason for the different behavior is Neo4j does not support case-insensitive indexes or constraints. Behavior changes with different types of uniqueness validations:

This will result in an n+1 query involving a regex match:

validates_uniqueness_of :name

If you want it to be case-insensitive, you can do this:

validates_uniqueness_of :name, case_sensitive:false

...and it will modify the Cypher match to be case-insensitive.

And, at the moment, this appears to not work at all and will be investigated:

validate :name, unique: true

Related to this, you should also be aware searches are case-sensitive, so Band.where(name: 'Ulver') and Band.where(name: 'ULVER') will give different results. To perform a regex search, do Band.where(name: /ulver/i), but be aware that this will bypass your indexes and may have a performance impact.

An option to work around this is to declare a uniqueness constraint on a second, normalized property. This duplicates some data but will offer a performance benefit when your database is large enough. You should also remember that Neo4j compresses and symbolizes strings when possible, so this additional data may not have any noticeable impact on disk footprint. An implementation of that may look like this:

class Student
  include Neo4j::ActiveNode

  property :name
  property :normalized_name, constraint: :unique

  validates_presence_of :name
  validate :set_normalized_name

  private

  def set_normalized_name
    self.normalized_name = self.name.downcase if self.name
  end
end

If the inability to use an index with a case-insensitive search bothers you and you'd rather not use this workaround, you can use the Searchkick gem to add Elasticsearch support to your app.

Clone this wiki locally