অ্যাক্টিভেকর্ড.ফাইন্ড (অ্যারে_ফিউডস), ​​সংরক্ষণের অর্ডার


101

আপনি যখন Something.find(array_of_ids)রেলগুলিতে করেন, ফলস্বরূপ অ্যারের ক্রম ক্রমের উপর নির্ভর করে না array_of_ids

অর্ডারটি সন্ধান এবং সংরক্ষণের কোনও উপায় আছে কি?

এটিএমের আইডির ক্রমের ভিত্তিতে এটিএম আমি ম্যানুয়ালি রেকর্ডগুলি বাছাই করি তবে এটি একধরনের খোঁড়া।

ইউপিডি: যদি প্যারাম :orderএবং কোনও ধরণের এসকিউএল ধারা ব্যবহার করে অর্ডার নির্দিষ্ট করা সম্ভব হয় , তবে কীভাবে?


উত্তর:


71

উত্তরটি কেবল মাইএসকিএলের জন্য

ফিল্ড () নামে মাইএসকিএলে একটি ফাংশন রয়েছে

আপনি এটি কীভাবে .find () এ ব্যবহার করতে পারেন তা এখানে:

>> ids = [100, 1, 6]
=> [100, 1, 6]

>> WordDocument.find(ids).collect(&:id)
=> [1, 6, 100]

>> WordDocument.find(ids, :order => "field(id, #{ids.join(',')})")
=> [100, 1, 6]

For new Version
>> WordDocument.where(id: ids).order("field(id, #{ids.join ','})")

আপডেট: এটি রেল 6.1 রেলের উত্স কোডে সরানো হবে


আপনি কি FIELDS()পোস্টগ্রিসের সমতুল্য জানতে পেরেছেন ?
ট্রুং Lê

4
পোস্টগ্রাজে এটি করার জন্য আমি একটি plpgsql ফাংশন লিখেছিলাম - omarqureshi.net/articles/2010-6-10-find-in-set-for-postgresql
ওমর কুরেশি

25
এটি আর কাজ করে না। আরও সাম্প্রতিক রেলের জন্য:Object.where(id: ids).order("field(id, #{ids.join ','})")
মাহেমফ

4
এটি গুনছারদের চেয়ে আরও ভাল সমাধান কারণ এটি পৃষ্ঠাবদ্ধতা ভাঙবে না।
পিগার্ডিরিও

.IDS আমার জন্য ভাল কাজ করে, এবং বেশ দ্রুত অ্যাক্টিভরেকর্ড ডকুমেন্টেশন
TheJKFever

80

অদ্ভুতভাবে, কেউ এরকম কিছু প্রস্তাব করেনি:

index = Something.find(array_of_ids).group_by(&:id)
array_of_ids.map { |i| index[i].first }

এসকিউএল ব্যাকএন্ড এটি করার পাশাপাশি এটি যতটা দক্ষ তা অর্জন করে।

সম্পাদনা করুন: আমার নিজের উত্তরের উন্নতি করতে আপনি এটি এর মতো করেও করতে পারেন:

Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values

#index_byএবং যথাক্রমে অ্যারে এবং হ্যাশগুলির জন্য অ্যাক্টিভসপোর্টে#slice বেশ সহজ সংযোজন ।


সুতরাং আপনার সম্পাদনাটি কাজ করছে বলে মনে হচ্ছে তবে এটি আমাকে হ্যাশটিতে নার্ভাস কী অর্ডার দেয় তবে এটির নিশ্চয়তা নেই? সুতরাং আপনি যখন স্লাইস কল করবেন এবং হ্যাশটিকে "পুনরায় অর্ডার করুন" পেলে এটি হ্যাশ ফিরিয়ে দেওয়ার মানগুলির উপর নির্ভর করে এটির কীগুলি যুক্ত হয়েছিল। এটি এমন একটি বাস্তবায়ন বিশদের উপর নির্ভর করে যা পরিবর্তিত হতে পারে like
জন

4
@ জন, অর্ডারটি রুবি ১.৯ এ গ্যারান্টিযুক্ত এবং অন্য যে কোনও বাস্তবায়ন যা এটি অনুসরণ করার চেষ্টা করে। ১.৮-এর জন্য, রেলস (অ্যাক্টিভসপোর্ট) হ্যাশ ক্লাসটিকে একইরকম আচরণ করতে প্যাচ করে, তাই আপনি যদি রেলগুলি ব্যবহার করেন তবে আপনার ঠিক আছে।
গুঞ্চারস

স্পষ্টতার জন্য ধন্যবাদ, সবেমাত্র এটি ডকুমেন্টেশনে পাওয়া গেছে।
জন

14
এটির সাথে সমস্যাটি হ'ল এটি সম্পর্কের পরিবর্তে একটি অ্যারে ফেরত দেয়।
ভেলিজার হরিস্টভ

4
দুর্দান্ত, তবে ওয়ান-লাইনারটি আমার পক্ষে কাজ করে না (রেল ৪.১)
বেসি

44

মাইক উডহাউস যেমন তার উত্তরে বলেছিলেন , হুডের নীচে এটি ঘটে যায়, রেলগুলি একটি কোয়েরিতে WHERE id IN... clauseসমস্ত রেকর্ড পুনরুদ্ধার করতে একটি এসকিউএল কোয়েরি ব্যবহার করে । এটি পৃথকভাবে প্রতিটি আইডি পুনরুদ্ধার করার চেয়ে দ্রুত, তবে আপনি লক্ষ্য করেছেন যে এটি আপনি যে রেকর্ডগুলি পুনরুদ্ধার করছেন তার ক্রম সংরক্ষণ করে না।

এটি ঠিক করার জন্য, আপনি রেকর্ড সন্ধানের সময় ব্যবহার করা আইডিগুলির মূল তালিকা অনুসারে আপনি অ্যাপ্লিকেশন স্তরে রেকর্ডগুলি বাছাই করতে পারেন।

অন্য অ্যারের উপাদান অনুসারে একটি অ্যারে বাছাই করার জন্য অনেক দুর্দান্ত উত্তরের ভিত্তিতে , আমি নিম্নলিখিত সমাধানটি সুপারিশ করি:

Something.find(array_of_ids).sort_by{|thing| array_of_ids.index thing.id}

অথবা আপনার যদি কিছুটা দ্রুত প্রয়োজন হয় (তবে তর্কযোগ্যভাবে কিছুটা কম পাঠযোগ্য) আপনি এটি করতে পারেন:

Something.find(array_of_ids).index_by(&:id).values_at(*array_of_ids)

4
দ্বিতীয় সমাধান (সূচক_বিহীন) আমার কাছে ব্যর্থ বলে মনে হচ্ছে, সমস্ত শূন্য ফলাফল তৈরি করে।
বেন হুইলার

23

এই জন্য কাজ বলে মনে হয় PostgreSQL ( উৎস ) - এবং একটি ActiveRecord সম্পর্ক ফেরৎ

class Something < ActiveRecrd::Base

  scope :for_ids_with_order, ->(ids) {
    order = sanitize_sql_array(
      ["position((',' || id::text || ',') in ?)", ids.join(',') + ',']
    )
    where(:id => ids).order(order)
  }    
end

# usage:
Something.for_ids_with_order([1, 3, 2])

অন্যান্য কলামগুলির জন্যও বাড়ানো যেতে পারে, যেমন nameকলামের জন্য, ব্যবহার করুন position(name::text in ?)...


আপনি সপ্তাহের আমার নায়ক। ধন্যবাদ!
এনটিডিবি

4
নোট করুন যে এটি কেবল তুচ্ছ মামলায় কাজ করে, আপনি অবশেষে এমন পরিস্থিতির বিরুদ্ধে দৌড়াবেন যেখানে তালিকার অন্য আইডির মধ্যে আপনার আইডি রয়েছে (উদাহরণস্বরূপ, এটি 11 টির মধ্যে 1 টি পাওয়া যাবে)। এর চারপাশের একটি উপায় হ'ল পজিশন চেকটিতে কমা যুক্ত করা এবং তারপরে যোগদানের ক্ষেত্রে একটি চূড়ান্ত কমা যুক্ত করা: অর্ডার = স্যানিটাইজ_এসকিএল_আরে (["অবস্থান (',' || ক্লায়েন্টসআইডি :: পাঠ্য || ', 'in?) ", ids.join (', ') +', '])
আইরিশডাবগুয়ে

ভালো কথা, @ আইরিশডাবগুই! আমি আপনার পরামর্শের ভিত্তিতে আমার উত্তর আপডেট করব। ধন্যবাদ!
জিঞ্জারাইম

আমার জন্য শৃঙ্খলা কাজ করে না। এখানে আইডির আগে টেবিলের নাম যুক্ত করা উচিত: এই জাতীয় পাঠ্য: ["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ','] আমার জন্য কাজ করা সম্পূর্ণ সংস্করণ: scope :for_ids_with_order, ->(ids) { order = sanitize_sql_array( ["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ','] ) where(:id => ids).order(order) } ধন্যবাদ @ ভার্জীলাইম @ আইরিশডবগুই
ব্যবহারকারী 1136228

আমার ধারণা আপনি যদি কিছু যোগ দেয় তবে আপনাকে টেবিলের নামটি যুক্ত করতে হবে ... আপনি যখন যোগদান করবেন তখন এটি অ্যাক্টিভেকর্ড স্কোপের সাথে বেশ সাধারণ।
জিঞ্জারাইম

19

আমি এখানে উত্তর হিসাবে , আমি সবেমাত্র একটি মণি মুক্তি ( আদেশ_as_specified ) যা আপনি এর মত স্থানীয় এসকিউএল অর্ডার করতে পারবেন:

Something.find(array_of_ids).order_as_specified(id: array_of_ids)

যতদূর আমি পরীক্ষা করতে সক্ষম হয়েছি, এটি সমস্ত আরডিবিএমএসে নেটিভভাবে কাজ করে এবং এটি একটি অ্যাক্টিভেকর্ড সম্পর্কটি জড়িত হতে পারে returns


4
বাবু, তুমি খুব দুর্দান্ত। ধন্যবাদ!
23-18

5

দুর্ভাগ্যক্রমে, এসকিউএল-তে সমস্ত ক্ষেত্রে কাজ করা সম্ভব নয়, আপনি প্রতিটি রেকর্ডের জন্য একক খোঁজ লিখতে হবে বা রুবিতে অর্ডার করতে হবে, যদিও মালিকানা কৌশলগুলি ব্যবহার করে এটির কাজ করার সম্ভবত একটি উপায় রয়েছে:

প্রথম উদাহরণ:

sorted = arr.inject([]){|res, val| res << Model.find(val)}

খুব অপরিহার্য

দ্বিতীয় উদাহরণ:

unsorted = Model.find(arr)
sorted = arr.inject([]){|res, val| res << unsorted.detect {|u| u.id == val}}

খুব দক্ষ না হলেও, আমি সম্মত হচ্ছি যে এই কাজটি ডিবি-অজিনস্টিক এবং গ্রহণযোগ্য যদি আপনার কাছে অল্প পরিমাণে সারি থাকে।
ট্রুং ল

এটির জন্য ইনজেকশন ব্যবহার করবেন না, এটি একটি মানচিত্র:sorted = arr.map { |val| Model.find(val) }
টোকল্যান্ড

প্রথমটি ধীর। আমি এই জাতীয় মানচিত্রের সাথে দ্বিতীয়টির সাথে একমত:sorted = arr.map{|id| unsorted.detect{|u|u.id==id}}
কুবুন

3

এখানে একটি মণি সন্ধান_আপনার সাথে রয়েছে যা আপনাকে স্থানীয় এসকিউএল কোয়েরি ব্যবহার করে দক্ষতার সাথে এটি করতে দেয়।

এবং এটি উভয় Mysqlএবং সমর্থন করে PostgreSQL

উদাহরণ স্বরূপ:

Something.find_with_order(array_of_ids)

আপনি যদি সম্পর্ক চান:

Something.where_with_order(:id, array_of_ids)

2

@ গুঞ্ছার্স উত্তরটি দুর্দান্ত, তবে এটি রেল ২.৩ এর বাক্সের বাইরে চলে না কারণ হ্যাশ শ্রেণীর আদেশ দেওয়া হয়নি। অর্ডারহ্যাশ ক্লাসটি index_byব্যবহারের জন্য একটি সহজ পরিশ্রম হ'ল এনিউমারেবল ক্লাস ' বাড়ানো:

module Enumerable
  def index_by_with_ordered_hash
    inject(ActiveSupport::OrderedHash.new) do |accum, elem|
      accum[yield(elem)] = elem
      accum
    end
  end
  alias_method_chain :index_by, :ordered_hash
end

এখন @ গুঞ্ছার্সের পদ্ধতির কাজ হবে

Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values

বোনাস

module ActiveRecord
  class Base
    def self.find_with_relevance(array_of_ids)
      array_of_ids = Array(array_of_ids) unless array_of_ids.is_a?(Array)
      self.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
    end
  end
end

তারপরে

Something.find_with_relevance(array_of_ids)

2

Model.pluck(:id)রিটার্ন ধরে নিচ্ছি [1,2,3,4]এবং আপনি অর্ডার চান[2,4,1,3]

ধারণাটি হ'ল ORDER BY CASE WHENএসকিউএল ধারাটি ব্যবহার করা । উদাহরণ স্বরূপ:

SELECT * FROM colors
  ORDER BY
  CASE
    WHEN code='blue' THEN 1
    WHEN code='yellow' THEN 2
    WHEN code='green' THEN 3
    WHEN code='red' THEN 4
    ELSE 5
  END, name;

রেলগুলিতে, আপনি একই মডেলটিতে একটি অনুরূপ কাঠামো তৈরির জন্য সর্বজনীন পদ্ধতি ব্যবহার করে এটি অর্জন করতে পারেন:

def self.order_by_ids(ids)
  if ids.present?
    order_by = ["CASE"]
    ids.each_with_index do |id, index|
      order_by << "WHEN id='#{id}' THEN #{index}"
    end
    order_by << "END"
    order(order_by.join(" "))
  end
else
  all # If no ids, just return all
end

তারপরে:

ordered_by_ids = [2,4,1,3]

results = Model.where(id: ordered_by_ids).order_by_ids(ordered_by_ids)

results.class # Model::ActiveRecord_Relation < ActiveRecord::Relation

এই সম্পর্কে ভাল জিনিস। ফলাফল ActiveRecord সম্পর্ক যেমন ফিরে হয় (যার ফলে আপনার মত পদ্ধতি ব্যবহার করতে last, count, where, pluck, ইত্যাদি)


1

হুডের নীচে, আইডির findএকটি অ্যারে SELECTসহ একটি WHERE id IN...ক্লজ সহ একটি উত্পন্ন করবে যা আইডির মাধ্যমে লুপিংয়ের চেয়ে আরও দক্ষ হওয়া উচিত।

সুতরাং অনুরোধটি ডেটাবেজে এক ট্রিপে সন্তুষ্ট, তবে ক্লজ SELECTছাড়াই ORDER BYসাফল্যযুক্ত নয়। অ্যাক্টিভেকর্ড এটি বুঝতে পারে, তাই আমরা findনিম্নলিখিত হিসাবে আমাদের প্রসারিত করি :

Something.find(array_of_ids, :order => 'id')

যদি আপনার অ্যারে আইডির ক্রমটি স্বেচ্ছাসেবী এবং তাৎপর্যপূর্ণ হয় (যেমন আপনি চান সারিগুলির ক্রমটি এতে থাকা আইডির ক্রম নির্বিশেষে আপনার অ্যারের সাথে মেলে ফিরে আসে) তবে আমি মনে করি যে ফলাফলগুলি পোস্ট-প্রসেসিং করে আপনি সেরা সার্ভার হবেন কোড - আপনি একটি :orderধারা তৈরি করতে পারেন তবে এটি প্রাণবন্ত জটিল হবে এবং উদ্দেশ্যপ্রণোদিতভাবে প্রকাশ করা মোটেই তা নয়।


নোট করুন যে বিকল্পগুলির হ্যাশটি হ্রাস করা হয়েছে। (দ্বিতীয় যুক্তি, এই উদাহরণে :order => id)
অক্টোবর 2:25 এ অক্টোডো

1

যদিও আমি এটি চ্যাঞ্জেলগের কোথাও উল্লেখ করা দেখতে পাচ্ছি না, মনে হচ্ছে সংস্করণ প্রকাশের সাথে এই কার্যকারিতাটি পরিবর্তিত হয়েছিল5.2.0

এখানে কমিট ডক্স আপডেট সঙ্গে বাঁধা 5.2.0তবে এটি হয়েছে বলে মনে ব্যাক-পোর্ট সংস্করণ মধ্যে 5.0



-4

((অর্ডার => '...') সন্ধানে একটি আদেশ শৃঙ্খলা রয়েছে যা রেকর্ড আনার সময় এটি করে। আপনি এখান থেকেও সহায়তা পেতে পারেন।

লিঙ্ক পাঠ্য

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