রেলস 3: র্যান্ডম রেকর্ড পান


132

সুতরাং, আমি রেল 2 এ এলোমেলো রেকর্ড সন্ধানের জন্য বেশ কয়েকটি উদাহরণ পেয়েছি - পছন্দসই পদ্ধতিটি মনে হয়:

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

নবাগত কিছু হওয়ার কারণে আমি নিশ্চিত নই যে এটি কীভাবে রেল 3-এ নতুন ফাইন্ড সিনট্যাক্স ব্যবহার করে তৈরি করা যেতে পারে।

সুতরাং, এলোমেলো রেকর্ডটি খুঁজতে "রেলস 3 ওয়ে" কী?


1
ডুপ্লিকেট stackoverflow.com/questions/2752231/...
fl00r

9
^^ ব্যতীত আমি বিশেষত রেলগুলি 3 সর্বোত্তম উপায়ের সন্ধান করছি , যা প্রশ্নের পুরো উদ্দেশ্য।
অ্যান্ড্রু

3 টি নির্দিষ্ট রেল শুধুমাত্র ক্যোয়ারী চেইন :)
fl00r

উত্তর:


216
Thing.first(:order => "RANDOM()") # For MySQL :order => "RAND()", - thanx, @DanSingerman
# Rails 3
Thing.order("RANDOM()").first

অথবা

Thing.first(:offset => rand(Thing.count))
# Rails 3
Thing.offset(rand(Thing.count)).first

প্রকৃতপক্ষে, 3 রিলে সমস্ত উদাহরণ কাজ করবে। RANDOMবড় টেবিলের জন্য তবে অর্ডারটি ব্যবহার করা বেশ ধীর কিন্তু আরও এসকিএল-স্টাইল

UPD। আপনি ইনডেক্সড কলামে পোস্টগ্রিএসএসকিউএল সিনট্যাক্সের মাধ্যমে নিম্নলিখিত কৌশলটি ব্যবহার করতে পারেন:

select * 
from my_table 
where id >= trunc(
  random() * (select max(id) from my_table) + 1
) 
order by id 
limit 1;

11
আপনার প্রথম উদাহরণটি মাইএসকিউএলে যদিও কাজ করবে না - মাইএসকিউএল এর বাক্যবিন্যাসটি থিং.ফার্স্ট (: অর্ডার => "র্যান্ড ()") (অ্যাক্টিভেকর্ড বিমূর্ততা ব্যবহার না করে এসকিউএল লেখার বিপদ)
ড্যানসিংগারম্যান

@ ড্যানসিংগারম্যান, হ্যাঁ এটি ডিবি নির্দিষ্ট RAND()বা RANDOM()। ধন্যবাদ
fl00r

এবং যদি সূচি থেকে আইটেমগুলি অনুপস্থিত থাকে তবে এটি সমস্যা তৈরি করবে না? (যদি স্ট্যাকের মাঝামাঝি কিছু মুছে ফেলা হয়, তবে কি এটির জন্য অনুরোধ করার কোনও সুযোগ থাকবে?
ভিক্টর এস

@ ভিক্টরস, না এটি # অফসেটটি কেবলমাত্র পরবর্তী উপলব্ধ রেকর্ডে যায় না। আমি এটি রুবি ১.৯.২ এবং রেল ৩.১ দিয়ে পরীক্ষা করেছি
সুডেসুনি

1
@ জনমারলিনো, হ্যাঁ 0 অফসেট, আইডি নয়। অফেট 0 এর অর্থ অর্ডার অনুসারে প্রথম আইটেম।
fl00r

29

আমি এমন একটি প্রকল্পে কাজ করছি ( রেলস 3.0.15, রুবি 1.9.3-পি 125-পারফ ) যেখানে ডিবি লোকালহোস্টে রয়েছে এবং ব্যবহারকারীদের সারণীতে 100K এর চেয়ে কিছু বেশি রেকর্ড রয়েছে

ব্যবহার

RAND দ্বারা আদেশ ()

বেশ ধীর

User.order ( "এন ডি (ID)")। প্রথম

হয়ে

নির্বাচন করুন users* usersর‌্যান্ডের অর্ডার থেকে (আইডি) সীমাবদ্ধ 1

এবং প্রতিক্রিয়া নিতে 8 থেকে 12 সেকেন্ড সময় নেয় !!

রেল লগ:

ব্যবহারকারী লোড (11030.8ms) নির্বাচন করুন users* usersর‌্যান্ডের মাধ্যমে অর্ডার থেকে () সীমাবদ্ধ 1

mysql এর ব্যাখ্যা থেকে

+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL | 110165 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+

আপনি দেখতে পাচ্ছেন যে কোনও সূচক ব্যবহার করা হয়নি ( সম্ভাব্য_কিজ = এনএলএল ), একটি অস্থায়ী সারণী তৈরি করা হয় এবং পছন্দসই মানটি পেতে অতিরিক্ত পাসের প্রয়োজন হয় ( অতিরিক্ত = অস্থায়ী ব্যবহার করে; ফাইলসোর্ট ব্যবহার করে )।

অন্যদিকে, ক্যোয়ারিকে দুটি অংশে বিভক্ত করে এবং রুবি ব্যবহার করে, প্রতিক্রিয়ার সময়টিতে আমাদের যুক্তিসঙ্গত উন্নতি হবে।

users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )

(; কনসোল ব্যবহারের জন্য নিল)

রেল লগ:

ব্যবহারকারী লোড (25.2ms) থেকে আইডি নির্বাচন usersব্যবহারকারী লোড (0.2ms) নির্বাচন করুন users। * থেকে usersকোথায় usersid= 106854 সীমাবদ্ধ 1

এবং মাইএসকিএল এর ব্যাখ্যা কেন প্রমাণ করে:

+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys | key                      | key_len | ref  | rows   | Extra       |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
|  1 | SIMPLE      | users | index | NULL          | index_users_on_user_type | 2       | NULL | 110165 | Using index |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+

+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

আমরা এখন কেবলমাত্র সূচি এবং প্রাথমিক কী ব্যবহার করতে পারি এবং প্রায় 500 গুণ দ্রুত কাজটি করতে পারি!

হালনাগাদ:

মন্তব্যগুলিতে আইক্যান্টবেকুল দ্বারা নির্দেশিত হিসাবে সারণীতে রেকর্ড মুছে ফেলা থাকলে উপরের সমাধানটির একটি ত্রুটি রয়েছে।

যে একটি workaround হতে পারে

users_count = User.count
User.scoped.limit(1).offset(rand(users_count)).first

যা দুটি প্রশ্নের অনুবাদ করে

SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794

এবং প্রায় 500ms এ চলে।


আপনার দ্বিতীয় উদাহরণে "শেষ" পরে ".id" যুক্ত করা একটি "আইডি ছাড়াই মডেলটি খুঁজে পেল না" ত্রুটি এড়ানো হবে। যেমন ইউজার.ফাইন্ড (ইউজারস.ফার্স্ট (র্যান্ডম.আর্যান্ড (ইউজারস লেংথ))। লাস্ট.আইডি)
টিউরিং_ম্যাচাইন

সতর্কবাণী! মাইএসকিউএলে RAND(id)আপনাকে প্রতিটি ক্যোয়ারিকে আলাদা এলোমেলো অর্ডার দেবে নাRAND()প্রতিটি ক্যোয়ারিতে আলাদা অর্ডার চাইলে ব্যবহার করুন ।
জাস্টিন ট্যানার

ইউজার.ফাইন্ড (users.first (Random.rand (users.length))। Last.id) কোনও রেকর্ড মুছে ফেলা থাকলে কাজ করবে না। [1,2,4,5,] এবং এটি 3 টির আইডি বাছাই করতে পারে তবে সক্রিয় রেকর্ডের সম্পর্ক থাকবে না।
আইস্যান্টবেকুল

এছাড়াও, ব্যবহারকারীরা = ব্যবহারকারী.স্কোপেড.সलेक्ट (: আইডি); শূন্য হয় না। পরিবর্তে এটি ব্যবহার করুন: ব্যবহারকারীগণ = ব্যবহারকারীর কোথাও (শূন্য)। নির্বাচন করুন (: আইডি)
আইস্যান্টবেকুল

আমি বিশ্বাস করি র‌্যান্ডম.আর্যান্ড (ইউজারস লেন্থ) প্রথমে প্যারামিটার হিসাবে ব্যবহার করা একটি বাগ is র্যান্ডম.র্যান্ড ০. ফিরে আসতে পারে যখন 0 প্রথমে প্যারামিটার হিসাবে ব্যবহৃত হয় তখন সীমাটি শূন্যে সেট করা থাকে এবং এটি কোনও রেকর্ড দেয় না। এর পরিবর্তে যেটি ব্যবহার করা উচিত তা হ'ল 1 + র‌্যান্ডম (ব্যবহারকারীদের দৈর্ঘ্য) ব্যবহারকারীগণের দৈর্ঘ্য> ০.
SWoo

12

Postgres ব্যবহার করা হয়

User.limit(5).order("RANDOM()")

মাইএসকিউএল ব্যবহার করা হয়

User.limit(5).order("RAND()")

উভয় দৃষ্টিতে আপনি ব্যবহারকারীদের সারণি থেকে এলোমেলোভাবে 5 টি রেকর্ড নির্বাচন করছেন। কনসোলে প্রদর্শিত প্রকৃত এসকিউএল কোয়েরি এখানে রয়েছে।

SELECT * FROM users ORDER BY RANDOM() LIMIT 5

11

এটি করার জন্য আমি একটি রেল 3 রত্ন তৈরি করেছি যা বড় টেবিলগুলিতে আরও ভাল পারফর্ম করে এবং আপনাকে সম্পর্ক এবং স্কোপগুলিকে শৃঙ্খলিত করতে দেয়:

https://github.com/spilliton/randumb

(সম্পাদনা): আমার মণির ডিফল্ট আচরণটি মূলত এখনকার মতো একই পদ্ধতির ব্যবহার করে তবে আপনি চাইলে পুরানো উপায়টি ব্যবহার করার বিকল্প রয়েছে :)


6

পোস্ট করা অনেক উত্তর আসলে বড় টেবিলগুলিতে (1+ মিলিয়ন সারি) ভাল পারফর্ম করতে পারে না। এলোমেলোভাবে দ্রুত অর্ডার করতে কয়েক সেকেন্ড সময় লাগে এবং টেবিলে একটি গণনা করতেও বেশ দীর্ঘ সময় লাগে।

এই পরিস্থিতিতে আমার পক্ষে ভাল কাজ করার একটি সমাধান হ'ল RANDOM()একটি শর্ত সহ ব্যবহার করা :

Thing.where('RANDOM() >= 0.9').take

দশ মিলিয়নেরও বেশি সারি সহ একটি টেবিলে, এই ক্যোয়ারীটি সাধারণত 2 মিমের কম লাগে takes


আপনার সমাধানের আরও একটি সুবিধা হ'ল ব্যবহারের takeফাংশন যা LIMIT(1)কোয়েরি দেয় তবে অ্যারের পরিবর্তে একক উপাদান ফেরত দেয় । সুতরাং আমাদের প্রার্থনা করার দরকার নেইfirst
পাইওটর গালাস

আমার কাছে মনে হয় টেবিলের শুরুতে রেকর্ডগুলি উচ্চতর সম্ভাব্যতা বেছে নিয়েছে এই পদ্ধতিতে, যা আপনি অর্জন করতে চান তা নাও হতে পারে।
Gorn

5

এখানে আমরা যেতে

রেলপথ

#in your initializer
module ActiveRecord
  class Base
    def self.random
      if (c = count) != 0
        find(:first, :offset =>rand(c))
      end
    end
  end
end

ব্যবহার

Model.random #returns single random object

বা দ্বিতীয় চিন্তা হয়

module ActiveRecord
  class Base
    def self.random
      order("RAND()")
    end
  end
end

ব্যবহার:

Model.random #returns shuffled collection

Couldn't find all Users with 'id': (first, {:offset=>1}) (found 0 results, but was looking for 2)
ব্রুনো

যদি কোনও ব্যবহারকারী না থাকে এবং আপনি 2 পেতে চান, তবে আপনি ত্রুটি পান। ধারণা তৈরী কর.
টিম ক্রেটসচার 13

1
দ্বিতীয় পদ্ধতি পোস্টগ্রাজের সাথে কাজ করবে না, তবে আপনি এর "RANDOM()"পরিবর্তে ব্যবহার করতে পারেন ...
ড্যানিয়েল রিখর

4

এটি আমার জন্য খুব দরকারী ছিল তবে আমার আরও কিছুটা নমনীয়তা প্রয়োজন, তাই আমি এটি করেছি:

কেস 1: একটি এলোমেলো রেকর্ড উত্স সন্ধান করুন:
ট্রেভর টার্ক সাইট এটি Thing.rb মডেলটিতে যুক্ত করুন

def self.random
    ids = connection.select_all("SELECT id FROM things")
    find(ids[rand(ids.length)]["id"].to_i) unless ids.blank?
end

তারপরে আপনার নিয়ামকটিতে আপনি এরকম কিছু কল করতে পারেন

@thing = Thing.random

কেস 2: একাধিক এলোমেলো রেকর্ড (কোন পুনরাবৃত্তি না) উত্স সন্ধান করা: মনে করতে পারে না যে
আমার পুনরাবৃত্তি না করে 10 টি এলোমেলো রেকর্ডের সন্ধান করা দরকার সুতরাং এটিই আমি কাজ করেছিলাম বলে জানলাম
আপনার নিয়ামক:

thing_ids = Thing.find( :all, :select => 'id' ).map( &:id )
@things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * rand ) } )

এটি 10 ​​টি এলোমেলো রেকর্ড সন্ধান করবে, তবে এটি উল্লেখ করার মতো যে যদি ডাটাবেসটি বিশেষত বড় হয় (লক্ষ লক্ষ রেকর্ড) তবে এটি আদর্শ হবে না এবং কার্য সম্পাদন ব্যাহত হবে। কয়েক হাজার রেকর্ড পর্যন্ত ভাল অভিনয় করবে যা আমার পক্ষে যথেষ্ট ছিল।


4

কোনও তালিকা থেকে এলোমেলোভাবে বাছাইয়ের রুবি পদ্ধতিটি samplesampleঅ্যাক্টিভেকর্ডের জন্য একটি দক্ষ তৈরি করতে চাই এবং পূর্ববর্তী উত্তরের উপর ভিত্তি করে আমি ব্যবহার করেছি:

module ActiveRecord
  class Base
    def self.sample
      offset(rand(size)).first
    end
  end
end

আমি এটি put lib/ext/sample.rbোকাই এবং তারপরে এটি দিয়ে লোড করুন config/initializers/monkey_patches.rb:

Dir[Rails.root.join('lib/ext/*.rb')].each { |file| require file }

আসলে, #countএকটি জন্য DB একটি কল করতে হবে COUNT। যদি রেকর্ডটি ইতিমধ্যে লোড করা থাকে তবে এটি একটি খারাপ ধারণা হতে পারে। #sizeপরিবর্তে একটি রিফ্যাক্টর ব্যবহার করা হবে কারণ এটি সিদ্ধান্ত নেবে যে #countব্যবহার করা উচিত কিনা, বা রেকর্ডটি ইতিমধ্যে লোড করা থাকলে ব্যবহার করতে হবে #length
বেনমোরগানআইও

আপনার মতামতের ভিত্তিতে স্যুইচ করা countহয়েছে size। আরও তথ্য এখানে: dev.mensfeld.pl/2014/09/…
ড্যান কোহন

3

5 কারাগারে কাজ করে এবং এটি ডিবি অজ্ঞেয়:

এটি আপনার নিয়ামকের মধ্যে:

@quotes = Quote.offset(rand(Quote.count - 3)).limit(3)

আপনি অবশ্যই অবশ্যই এটিকে এখানে উদ্বেগের মধ্যে ফেলে যেতে পারেন ।

অ্যাপ্লিকেশন / মডেল / উদ্বেগ / randomable.rb

module Randomable
  extend ActiveSupport::Concern

  class_methods do
    def random(the_count = 1)
      records = offset(rand(count - the_count)).limit(the_count)
      the_count == 1 ? records.first : records
    end
  end
end

তারপর ...

অ্যাপ্লিকেশন / মডেল / book.rb

class Book < ActiveRecord::Base
  include Randomable
end

তারপরে আপনি এগুলি করে কেবল ব্যবহার করতে পারবেন:

Books.random

অথবা

Books.random(3)

এটি সর্বদা পরবর্তী রেকর্ড গ্রহণ করে, যা কমপক্ষে নথিবদ্ধ হওয়া প্রয়োজন (কারণ এটি ব্যবহারকারী যা চায় তা নাও হতে পারে)।
Gorn

2

আপনি অ্যাক্টিভেকর্ডে নমুনা () ব্যবহার করতে পারেন

যেমন

def get_random_things_for_home_page
  find(:all).sample(5)
end

সূত্র: http://thinkingeek.com/2011/07/04/easily-select-random-records-rails/


33
এটি আপনার কাছে প্রচুর পরিমাণে রেকর্ড রয়েছে তবে এটি ব্যবহার করা খুব খারাপ প্রশ্ন, কারণ ডিবি সমস্ত রেকর্ড নির্বাচন করবে, তারপরে রেলগুলি সেই থেকে পাঁচটি রেকর্ড বেছে নেবে - ব্যাপকভাবে অপচয়যোগ্য।
ডেভস্টেফেন্স

5
sampleঅ্যাক্টিভেকর্ডে নেই, নমুনা অ্যারেতে রয়েছে। api.rubyonrails.org/classes/Array.html#method-i-sample
ফ্রান্স

3
এটি একটি র্যান্ডম রেকর্ড পাওয়ার জন্য একটি ব্যয়বহুল উপায়, বিশেষত একটি বড় টেবিল থেকে। রেলগুলি আপনার টেবিল থেকে মেমরিতে প্রতিটি রেকর্ডের জন্য একটি বস্তু লোড করবে। আপনার যদি প্রমাণের প্রয়োজন হয় তবে 'রেলস কনসোল' চালান, 'সামমোডেলফ্রয়মোর অ্যাপ.ফাইন্ড (: সমস্ত)। নমুনা (5)' ব্যবহার করে উত্পাদিত এসকিউএল দেখুন।
এলিয়ট

1
আমার উত্তরটি দেখুন, যা একাধিক এলোমেলো রেকর্ড পাওয়ার জন্য এই ব্যয়বহুল উত্তরটিকে একটি প্রবাহিত সৌন্দর্যে রূপান্তরিত করে।
আর্কিলে


1

এলোমেলো রেকর্ডগুলির জন্য এই রত্নটিকে দৃ St়ভাবে সুপারিশ করুন, যা প্রচুর ডেটা সারি সহ টেবিলের জন্য বিশেষভাবে ডিজাইন করা হয়েছে:

https://github.com/haopingfan/quick_random_records

এই মণি ব্যতীত অন্য সমস্ত উত্তর বড় ডেটাবেস সহ খারাপভাবে সম্পাদন করে:

  1. quick_random_records কেবল 4.6msসম্পূর্ণ ব্যয় ।

এখানে চিত্র বর্ণনা লিখুন

  1. গৃহীত উত্তর User.order('RAND()').limit(10)খরচ 733.0ms

এখানে চিত্র বর্ণনা লিখুন

  1. দ্য offsetপদ্ধতির খরচ 245.4msসম্পূর্ণভাবে।

এখানে চিত্র বর্ণনা লিখুন

  1. User.all.sample(10)পদ্ধতির খরচ 573.4ms

এখানে চিত্র বর্ণনা লিখুন

দ্রষ্টব্য: আমার টেবিলটিতে কেবল 120,000 জন ব্যবহারকারী রয়েছে। আপনার যত বেশি রেকর্ড রয়েছে, পারফরম্যান্সের পার্থক্য তত বেশি হবে।


হালনাগাদ:

550,000 সারি দিয়ে টেবিলের উপর পারফর্ম করুন

  1. Model.where(id: Model.pluck(:id).sample(10)) মূল্য 1384.0ms

এখানে চিত্র বর্ণনা লিখুন

  1. gem: quick_random_recordsশুধুমাত্র ব্যয় 6.4msসম্পূর্ণ

এখানে চিত্র বর্ণনা লিখুন


-2

টেবিল থেকে একাধিক এলোমেলো রেকর্ড পাওয়ার খুব সহজ উপায়। এটি 2 সস্তা প্রশ্ন করে makes

Model.where(id: Model.pluck(:id).sample(3))

আপনি চান এমন এলোমেলো রেকর্ডের সংখ্যায় "3" পরিবর্তন করতে পারেন।


1
না, Model.pluck (: id)। নমুনা (3) অংশটি সস্তা নয়। এটি সারণীতে প্রতিটি উপাদানগুলির জন্য আইডি ফিল্ডটি পড়বে।
ম্যাক্সিমিলিয়ানো গুজম্যান

একটি দ্রুত ডেটাবেস-অজ্ঞানীয় উপায় আছে?
আর্কলয়ে

-5

আমি কেবল এই সমস্যাটি নিয়ে একটি ছোট অ্যাপ্লিকেশন বিকাশ করেছি যেখানে আমি আমার ডিবি থেকে একটি এলোমেলো প্রশ্ন নির্বাচন করতে চেয়েছিলাম। আমি ব্যবহার করতাম:

@question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1]

এবং এটা আমার জন্য ভাল কাজ করছে। এটি বৃহত্তর ডিবিগুলির জন্য পারফরম্যান্স যেহেতু এটি কেবলমাত্র একটি ছোট অ্যাপ্লিকেশন speak


হ্যাঁ, এটি কেবল আপনার সমস্ত রেকর্ড পাচ্ছে এবং সেগুলিতে রুবি অ্যারে পদ্ধতি ব্যবহার করছে। ত্রুটিটি অবশ্যই রয়েছে যে এর অর্থ আপনার সমস্ত রেকর্ডগুলি মেমরিতে লোড করা, তারপরে এলোমেলোভাবে তাদের পুনরায় অর্ডার করা, তারপরে পুনরায় সাজানো অ্যারেতে দ্বিতীয় আইটেমটি ধরা। যদি আপনি কোনও বড় ডেটাসেট নিয়ে কাজ করে থাকেন তবে তা অবশ্যই মেমরির হোগ হতে পারে। নাবালিকাকে আলাদা করে রাখলে, কেন প্রথম উপাদানটি ধরবে না? (উদা। shuffle[0])
অ্যান্ড্রু

অবশ্যই পরিবর্তন হতে হবে [0]
মার্সেলো অস্ট্রিয়া
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.