特定のレコードを含むランダムな ActiveRecord
概要
条件を満たし、一部のレコードを除外するランダムなレコードを 1 つ返すモデルのクラス関数を作成したいと考えています。 「ランダム記事セクション」を作ろうと考えています。
私の関数は次のようにしたいのですが
Article.randomArticle([1, 5, 10]) # array of article ids to exclude
いくつかの疑似コード:
ids_to_exclude = [1,2,3]
loop do
returned_article = Article.where(published: true).sample
break unless ids_to_exclude.include?(returned_article.id)
do
解決策
DB 固有のオプションを見てみましょう。
class Article
# ...
def self.random(limit: 10)
scope = Article.where(published: true)
# postgres, sqlite
scope.limit(limit).order('RANDOM()')
# mysql
scope.limit(limit).order('RAND()')
end
end
Article.random は、データベースに 10 個のランダムなレコードを取得するよう要求します。 それでは、いくつかのレコードを除外するオプションを追加する方法を見てみましょう。
class Article
# ...
def self.random(limit: 10, except: nil)
scope = Article.where(published: true)
if except
scope = scope.where.not(id: except)
end
scope.limit(limit).order('RANDOM()')
end
end
これで、Article.random(例外: [1,2,3]) は、ID が [1,2,3] ではない 10 個のレコードを取得します。
これは、rails の .where がチェーン可能なスコープを返すためです。例えば:
> User.where(email: '[email protected]').where.not(id: 1)
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 AND ("users"."id" != $2) [["email", "[email protected]"], ["id", 1]]
=> #<ActiveRecord::Relation []>
ここでスコープを渡すこともできます。
# cause everyone hates Bob
Article.random(except: Article.where(author: 'Bob'))
ここで DB 固有のソリューションが適切な選択である理由については、「Rails Quick Tips - Random Records」を参照してください。