This project is archived and is in readonly mode.

#2974 ✓wontfix
Joris

Add find :unique to ActiveRecord

Reported by Joris | July 30th, 2009 @ 03:46 PM

The current method of getting the result of a query is find :fisrt.
This will essentially do a query with max 1 row.

However, sometimes a developer creates a query that doesn't work the way he thought it would.

My suggestion is to add a new find :unique

This should do the basically same as find :first, except that it should not limit the number of records.
If more than 1 was found, it should raise an exception stating that there are too many records (and also the number of records found, just to inform the dev)

find :first should be deprecated for the use of getting a unique row from a query.

:first and :last have the same type of use. Finding the first or last in a list.

For example:

Person.find :unique, :conditions => { :ssid => ssid }
vs
Person.find :first, :conditions => { :ssid => ssid }

Also, the method_missing way of finding should use the unique.

Comments and changes to this ticket

  • Joris

    Joris July 30th, 2009 @ 03:57 PM

    So, I read the bar at the right :)
    So, I didn't test this, I just typed it here, so I'm not sure if my manual diff is ok. anyway.. here's the code:

    In base.rb from activerecord:

    in find(*args):

        case args.first
          when :first  then find_initial(options)
          when :all    then find_every(options)
    
    •   when :unique then find_unique(options)
      

    add method:

        def find_initial(options)
          options.update(:limit => 1) unless options[:include]
          find_every(options).first
        end
    
    •  def find_unique(options)
      
    •    records = find_every(options)
      
    •    if records.size > 0 then
      
    •      raise ActiveRecordError, "Too many records found: #{records.size}"
      
    •    end
      
    •    records.first
      
    •  end
      

    Also, some calls to find_initial should be replaced by find_unique, but I can imagine that would break some existing apps. So that might be for later.

  • Joris

    Joris July 30th, 2009 @ 04:01 PM

    So, I read the bar at the right :)
    So, I didn't test this, I just typed it here, so I'm not sure if my manual diff is ok. anyway.. here's the code:

    In base.rb from activerecord:

    in find(*args):

            case args.first

          when :first  then find_initial(options)
          when :all    then find_every(options)
    
    
    
    
    •   when :unique then find_unique(options)</code>
      
    
    
    
    
    add method:
            def find_initial(options)

          options.update(:limit =&gt; 1) unless options[:include]
          find_every(options).first
        end
    
    
    
    
    •  def find_unique(options)
      
    •    records = find_every(options)
      
    •    if records.size &gt; 0 then
      
    •      raise ActiveRecordError, &quot;Too many records found: #{records.size}&quot;
      
    •    end
      
    •    records.first
      
    •  end</code>
      
    
    
    
    
    Also, some calls to find_initial should be replaced by find_unique, but I can imagine that would break some existing apps. So that might be for later.
  • Pratik

    Pratik July 30th, 2009 @ 04:58 PM

    Hey Joris,

    I think you should discuss this change in the ML first. Also, your patch should have tests. Check http://guides.rubyonrails.org/contributing_to_rails.html for more details.

    Thanks!

  • Pratik

    Pratik July 30th, 2009 @ 05:01 PM

    • State changed from “new” to “incomplete”
  • MatthewRudy

    MatthewRudy July 31st, 2009 @ 11:53 AM

    is this really the proposed implementation?

    def find_unique(options)
      records = find_every(options)
      if records.size > 0 then

    raise ActiveRecordError, &quot;Too many records found: #{records.size}&quot;
    
    
    
    
    end records.first end
    Seems a bit crazy,
    I'd rather have the two db calls (count, and find_first)

    Also, I don't get the use-case.
    Joris, do you have some examples of how you are using this feature?

    The only reason I've needed something similar is this case;

    class SomeSortOfSettings < ActiveRecord::Base
      def self.instance
        self.first || self.new
      end
    end
    

    namely a "Singleton" ActiveRecord,
    but I think there's a better way to deal with this
    (an ActiveRecord::Singleton class, perhaps)

    User.find(:unique, :conditions => {:username => "ralph"})
    

    is silly,
    you should just have a unique key on :username

  • Joris

    Joris July 31st, 2009 @ 12:00 PM

    Having 2 queries would be a waste of db calls. If you expect 1 row, there's no need to do a count.

    All queries for something with a unique index / natural key is where I'd do this. Just because it's contextual more correct, and it has no performance drawbacks except for a single check if there's another row in the resultset.

    The implementation was just something I typed. I can imagine that there would be some other things that would need to happen, but basically that's how about what I had in mind.

    Hibernate has a similar feature: query.uniqueResult

  • Pratik

    Pratik July 31st, 2009 @ 12:01 PM

    • State changed from “incomplete” to “wontfix”

    While I don't think this is gonna make it to the core, but the best implementation would probably be the one using :limit => 2.

    This seem very project specific, hence you should just keep it in /lib or maybe make a plugin and see if many people find it useful or not. We can always reconsider the feature if more people are using it.

    Thanks.

  • Ryan Bigg

    Ryan Bigg October 11th, 2010 @ 10:55 AM

    • Tag cleared.
    • Importance changed from “” to “Low”

    Automatic cleanup of spam.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages