Jump to content

The ultimate community for Ruby on Rails developers.


Photo

Retrieving a random record - any other suggestion?


  • Please log in to reply
11 replies to this topic

#1 Rowel

Rowel

    Controller

  • Members
  • 109 posts

Posted 14 October 2013 - 01:12 AM

I'm retrieving a random record from a model using this technique:  (note: database is postgres) 

 

in my model, I have a default_scope ordered by RANDOM()

class Bibleverse < ActiveRecord::Base
  # order by random
  default_scope -> { order('RANDOM()') }
end

then I can just do something like this to retrieve a random record

@bibleverse = Bibleverse.limit(1)

QUESTION: 

Is this okay to do? using order by RANDOM() 

 

Is there another technique that is more correct/general/Rails way/cross-database compatible? 

 

 



#2 Rowel

Rowel

    Controller

  • Members
  • 109 posts

Posted 14 October 2013 - 01:28 AM

Wow, this is weird... 

 

If I do this, 

@bibleverse = Bibleverse.limit(1)

the return result is 

#<ActiveRecord::Relation [#<Bibleverse id: 5, versetext: ......blah blah blah etc.... created_at: nil, updated_at: nil>]>

I can't do a 

@bibleverse = Bibleverse.limit(1).id 

I get an error. "NoMethodError"

 

 

As a fix, I have to add a ".first", before I can access the fields/columns.  

@bibleverse = Bibleverse.limit(1).first.id 


#3 Greg Molnar

Greg Molnar

    Passenger

  • Members
  • 2 posts

Posted 14 October 2013 - 05:08 AM

Even with limit the query returns an array as you see from this output:

#<ActiveRecord::Relation [#<Bibleverse id: 5, versetext: ......blah blah blah etc.... created_at: nil, updated_at: nil>]>

Hence you need to use first.



#4 stevieing

stevieing

    Dispatcher

  • Members
  • 40 posts

Posted 14 October 2013 - 09:07 AM

Scope will always return an ActiveRecord::Relation object.

 

You are probably going the long way round for what you need to do as you are selecting all of the records and then limiting to one.

 

If you want to select a single record then use find or find_by, much more efficient and it will return the correct object type.

 

I am sure there is a way to select an id or some other attribute randomly e.g. any number between 1 and the highest id. Maybe use a class method.

 

Steve.



#5 Rowel

Rowel

    Controller

  • Members
  • 109 posts

Posted 14 October 2013 - 05:08 PM

Ahhh thanks, that explains it.  Didn't notice the [ ]. 



#6 Ohm

Ohm

    Guard

  • Members
  • 179 posts
  • LocationCopenhagen

Posted 15 October 2013 - 05:31 AM

You should do something like

c = Bibleverse.count
Bibleverse.find(:first, :offset => rand(c))

as it will be a lot faster on big tables. Note that this is two SQL queries instead of one.

 

If you want to use your approach, you do not need limit(1) with first, as first already puts a limit(1) on an ActiveRelation.


  • katafrakt and Bharat Soni like this

Blog: http://ohm.sh | Twitter: madsohm


#7 Bharat Soni

Bharat Soni

    Dispatcher

  • Members
  • 54 posts
  • LocationAhmedabad

Posted 15 October 2013 - 05:44 AM

Great answer...........

 

But with both the query it will not take more time because one will send only count.....


Fun with Ruby on Rails


#8 Rowel

Rowel

    Controller

  • Members
  • 109 posts

Posted 15 October 2013 - 06:11 AM


If you want to use your approach, you do not need limit(1) with first, as first already puts a limit(1) on an ActiveRelation.

 

 

 

 
Thanks... yes, that works. No need for limit(1)
 
 
c = Bibleverse.count
Bibleverse.find(:first, :offset => rand(c))

 

 

This produces this warning in rails console

DEPRECATION WARNING: Calling #find(:first) is deprecated. Please call #first directly instead. You have also used finder options. These are also deprecated. Please build a scope instead of using finder options. (called from irb_binding at (irb):37)


#9 Bharat Soni

Bharat Soni

    Dispatcher

  • Members
  • 54 posts
  • LocationAhmedabad

Posted 15 October 2013 - 06:29 AM

Use first directly instead of find this will remove the warning...................

c = Bibleverse.count
Bibleverse.first(:offset => rand(c))

  • Rowel likes this

Fun with Ruby on Rails


#10 Ohm

Ohm

    Guard

  • Members
  • 179 posts
  • LocationCopenhagen

Posted 15 October 2013 - 07:03 AM

Sorry, my bad. Do it like bharat.soni14 says or like

c = Bibleverse.count
Bibleverse.offset(rand(c)).first

(Depending on which one you find the prettiest)


  • Rowel likes this

Blog: http://ohm.sh | Twitter: madsohm


#11 Rowel

Rowel

    Controller

  • Members
  • 109 posts

Posted 15 October 2013 - 10:13 PM

Sorry, my bad. Do it like bharat.soni14 says or like

c = Bibleverse.count
Bibleverse.offset(rand(c)).first

(Depending on which one you find the prettiest)

 

Thanks ohm. This one works, without any warning. 

 

 

Bharat, this still gives a warning about deprecated code. 

Bibleverse.first(:offset => rand(c))

DEPRECATION WARNING: Relation#first with finder options is deprecated. Please build a scope and then call #first on it instead.



#12 Bharat Soni

Bharat Soni

    Dispatcher

  • Members
  • 54 posts
  • LocationAhmedabad

Posted 16 October 2013 - 05:32 AM

ok Rowel.................Thanks for the information...

B)


Fun with Ruby on Rails





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users