Natijada potentsial ikki modelni qaytarish uchun Rails so'rovini qanday majburlay olaman?

Men Rails-dan foydalanayapman. Men buni tekshiruvchi modelida mezonlarga asosan muayyan modeli o'rnatish uchun qo'lladim ...

  @results = MyObjectTime.joins(:my_object,
                            "LEFT JOIN user_my_object_time_matches on my_object_times.id = user_my_object_time_matches.my_object_time_id #{additional_left_join_clause}")
                     .where( search_criteria.join(' and '), *search_values )
                     .limit(1000) 
                     .order("my_objects.day DESC, my_objects.name")
                     .paginate(:page => params[:page])

Mening savolim quyidagicha: @results array "MyObjectTime" va "UserMyObjectTimeMatch" bilan bog'langan bo'lishi uchun yuqoridagi so'zlarni qanday qilib qayta yozaman?

9
"#Includes (: user_my_object_time_matches)" barcha assotsiatsiyalarni qiziqtiradimi? Bu men istagan narsa emas, va mening so'rovim nima emasligi. So'rovlar faqat MyObjectTime ob'ektiga bitta bog'lanishni yuklaydi, bu men istagancha yuklamoqchi bo'laman.
qo'shib qo'ydi muallif Dave, manba
Sizga yordam berishga yordam beradigan ba'zi bir misollarga yordam bera olasizmi?
qo'shib qo'ydi muallif Milind, manba
Sizning @results o'zgaruvchisi MyObjectTime misollari bilan numaralandırılabilir. MyObjectTime namunasi va UserMyObjectTimeMatchning biriktirilgan nusxalari bo'lgan ob'ekt yo'q. 1000 + 1 so'rovlarini chiqarish muammosi assotsiatsiyani istagancha yuklab olish yo'li bilan hal qilinadi: #includes (: user_my_object_time_matches)
qo'shib qo'ydi muallif David Aldridge, manba
ActiveRecord modelingiz uchun kodni bera olasizmi? Ehtimol, uyushmalar va joylar to'g'ri kombinatsiyasini yaratish muammoingizni hal qiladi va sizning kodingizni juda ko'p ukish mumkin bo'ladi. Uyushmalarning doiralari: guides.rubyonrails.org/… Darslar: api.rubyonrails.org/classes/ ActiveRecord/Klaster/Named/& hellip;
qo'shib qo'ydi muallif marksiemers, manba

6 javoblar

Men muqobilni qidirishni tavsiya qilaman, lekin o'zingiz taqdim etgan va sizning sharhingizda keltirilgan potentsial 10001 so'rovlarini bartaraf etishni xohlagan holda, agar sizda has_many 'user_my_object_time_matches' sozlamasi mavjud bo'lsa:

@results = MyObjectTime.joins(:my_object,
                            "LEFT JOIN user_my_object_time_matches on my_object_times.id = user_my_object_time_matches.my_object_time_id #{additional_left_join_clause}")
        .where( search_criteria.join(' and '), *search_values )
        .limit(1000) 
        .order("my_objects.day DESC, my_objects.name")
        .paginate(:page => params[:page])
        .includes(:user_my_object_time_matches)
        .map{|t| [t, t.user_my_object_time_matches]}
3
qo'shib qo'ydi
FYi ushbu qatorni tuzmaydi - ".map | t | [t, t.user_my_object_time_matches}}". Nima uchun "xarita" qatoriga kerak?
qo'shib qo'ydi muallif Dave, manba
Bu {. Kodni tahrir qildim
qo'shib qo'ydi muallif s1mpl3, manba

Sen qila olmaysan. Hech bo'lmasa ActiveRecord yoki Rails'ning standart interfeysidan foydalanmaslik. ActiveRecord so'rovlar uslublari faqatgina Modelni chaqirish ob'ektlarini qaytarib beradigan tarzda ishlab chiqilgan.

Misol uchun, Agar siz so'ragan bo'lsangiz

MyObjectTime.joins(:time).where(parent_id: 5)

ob'ektlar faqat MyObjectTime uchun qaytib keladi. Biroq, join tufayli, time dan olingan yozuvlar ham qaytarilishi mumkin, faqat qaytarilmaydi. Shunday qilib, undan foydalanishingiz mumkin. Ayniqsa joins o'rniga o'z ichiga olgan foydalanayotgan bo'lsangiz, bog'liq modellar olinadi va siz ularni biriktiruvchi yozuv/ob'ekt bo'yicha murojaat qilishingiz mumkin.

Natijada juftligini yaratish uchun tushuntirish

Bu kerakli natijalarga ega bo'lgan xashni yaratish orqali osonlik bilan amalga oshirilishi mumkin.

Masalan, answer_sheet assotsiatsiyasiga ega bo'lgan Mark modelini ko'rib chiqing.

You can fetch the marks with :answer_sheet using includes this way. I'm fetching 20 in the example.

marks = Mark.limit(20).includes(:answer_sheet);

Bu esa, javoblar orqali qabul qilinadigan answer_sheetni keltiradi, Shunday qilib, bu bilan aralashmalarni yaratish

h = {}
marks.each do |mark|
  h[mark.id] = {}
  h[mark.id][:mark] = mark
  h[mark.id][:answer_sheet] = mark.answer_sheet
end

Endi sizning karmoningiz mark.id tugmachasi orqali tayyorlangan belgisi va answer_sheet obyektiga ega.

Bu faqat dastlabki ikkita so'rovda bajariladi va iteratsiya boshqa so'rovlarni keltirib chiqarmaydi. Mening tizimimda faqat kerakli so'rovlar ( o'z ichiga oladi yordamida)

SELECT  "marks".* FROM "marks" LIMIT 20
  AnswerSheet Load (0.9ms)  SELECT "answer_sheets".* FROM "answer_sheets" WHERE "answer_sheets"."mark_id" IN (877, 3035, 3036, 878, 879, 880, 881, 561, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893)

Nishon ob'ektini ham kalit sifatida ishlatishingiz mumkin. Keyin qurilish jarayoni yanada osonlashadi

h = {}
marks.each do |mark|
  h[mark] = mark.answer_sheet
end

Endi marka bilan bog'langan answer_sheet ga kirishni xohlagan vaqtingizda uni olish uchun h [mark] kodini ishlatishingiz kerak.

3
qo'shib qo'ydi
"Agar siz natijalarni juftlarni qurish uchun ishlatishingiz mumkin" deb hisoblasangiz, unda men ilgari ishlab chiqarilgan taxtga o'xshash so'rovlar miqdorini yaratmasdan qanday qilib buni amalga oshirishni xohlamayman.
qo'shib qo'ydi muallif Dave, manba
Uni ActiveRecord-dan foydalanish shart emas, men uni kodimga kiritgan edim, chunki buni amalga oshirishning boshqa yo'llarini o'ylay olmadim. Agar ActiveRecordga tegishli bo'lmagan boshqa usul bo'lsa, bu mening fikrim.
qo'shib qo'ydi muallif Dave, manba
Railsning so'rovlar interfeysi ActiveRecord ustiga qurilgan va sizning talabingizni qo'llab-quvvatlamaydi. Lekin aytganimdek, siz natijani juftlarni yaratish uchun ishlatishingiz mumkin
qo'shib qo'ydi muallif Anwar, manba
@Men misolni qo'shishga harakat qildim. Iteratsiya includes bilan so'rovlarni yaratmaydi
qo'shib qo'ydi muallif Anwar, manba
@grizzthedj IN qaerda?
qo'shib qo'ydi muallif Anwar, manba
@grizzthedj Rahmat. Men postgres chegarasi haqida aniq bilmayman, shundaymi? Bundan tashqari, siz barcha aloqa o'rnatish uchun murakkab so'rovlar yaratadigan eager_load dan foydalanishingiz mumkin.
qo'shib qo'ydi muallif Anwar, manba
@grizzthedj Men ushbu javobni topdim stackoverflow.com/a/1010158/1039893 va postgres ishlamaydi
qo'shib qo'ydi muallif Anwar, manba
@ Anvar: Etarlicha adolatli, bilish yaxshi! Savoldagi ma'lumotlar bazasi hech qachon aniqlanmagan, shuning uchun u erda tashlab ketilgan, chunki u yokiacle, SQL serverni bilaman va cheklovlar bor. Menimcha, MySQL max_allowed_packet qiymati bilan cheklangan, bu o'zgaruvchi bo'lishi mumkin.
qo'shib qo'ydi muallif grizzthedj, manba
Pochtaingizda AnswerSheet Load (0.9ms) ... ning chiqish so'roviga qarang
qo'shib qo'ydi muallif grizzthedj, manba
Ikkinchi so'rov bilan bog'liq muammo IN qoidasini ishlatishidir. Ko'p (agar barchasi bo'lmasa) relaksial ma'lumotlar bazalari in moddasida ishlatilishi mumkin bo'lgan qiymatlar soniga bog'liq.
qo'shib qo'ydi muallif grizzthedj, manba

Faqat siz xohlagancha jadvallardan ustunlar kiritish imkonini beradigan ActiveRecord aloqasidan foydalanib, xom SQL so'rovini bajarishingiz mumkin. Har bir narsani bir so'rovda olishingiz mumkin. Boshqa nomdagi noma'lum ustun nomlari (masalan, mening namunamda name ustuni uchun qilgan kabi)

Sizning modellaringiz qanday ko'rinishini bilmayman, lekin bu erda oddiy ota-onaning namunasi:

Modellar va migratsiya yaratish

# testmigration.rb
class Testtables < ActiveRecord::Migration[5.0]
  def change
    create_table :parents do |t|
      t.string :name
      t.timestamps
    end

    create_table :siblings do |t|
      t.string :name
      t.references :parent
      t.timestamps
    end
  end
end

# parent.rb
class Parent < ApplicationRecord
  has_many :siblings
end

# sibling.rb
class Sibling < ApplicationRecord
end

Sinov ma'lumotlarini yarating

> rails c
> Parent.new(name: "Parent A").save!
> Parent.new(name: "Parent B").save!
> Sibling.new(name: "Sibling 1 - Parent A", parent_id: 1).save!
> Sibling.new(name: "Sibling 2 - Parent A", parent_id: 1).save!
> Sibling.new(name: "Sibling 1 - Parent B", parent_id: 2).save!
> Sibling.new(name: "Sibling 2 - Parent B", parent_id: 2).save!
> Sibling.new(name: "Sibling 3 - Parent B", parent_id: 2).save!

Ham ota-ona, ham qardosh modellardan (ism va yaratilgan_at) ustunlarni o'z ichiga olgan maxsus so'rovni ishga tushiring.

> sql_query = "SELECT p.name as parent_name, p.created_at as parent_created, s.name as sibling_name, s.created_at as sibling_created FROM public.parents p INNER JOIN public.siblings s on s.parent_id = p.id;"
> result = ActiveRecord::Base.connection.execute(sql_query)

Natijalarni tekshiring

> result[0]['parent_name']
  => "Parent A" 
> result[0]['sibling_name']
  => "Sibling 1 - Parent A"
> result[1]['parent_created']
  => "2017-03-04 18:31:54.661714"
2
qo'shib qo'ydi
Yaxshi! Lekin nima uchun AR bilan buni qilish yaxshiroq deb o'ylaysiz? AR bilan foydalanish hali ham osonroq!
qo'shib qo'ydi muallif Anwar, manba
Natijada ikkala model ham bormi? Misolni o'z ichiga olsangiz foydali bo'lishi mumkin
qo'shib qo'ydi muallif Anwar, manba
Misol bilan yangilangan javob
qo'shib qo'ydi muallif grizzthedj, manba
Yo'q. Bu "relslar" usuli emas, balki oddiygina ushbu variantni muqobil ravishda ko'rsatib turibdi, chunki siz maxsus so'rovni bajarishingiz kerak va ActiveRecord ehtiyojlarni qondira olmaydi.
qo'shib qo'ydi muallif grizzthedj, manba

Bu yerda egerloadingdan foydalanishingiz mumkin, natijada bir nechta natijani bir qatorda birlashtirish mumkin:

@results = MyObjectTime.joins(:my_object,
                            "LEFT JOIN user_my_object_time_matches on my_object_times.id = user_my_object_time_matches.my_object_time_id #{additional_left_join_clause}")
                     .where( search_criteria.join(' and '), *search_values )
                     .limit(1000) 
                     .order("my_objects.day DESC, my_objects.name")
                     .paginate(:page => params[:page]).includes(:user_my_object_time_matches)

Bir marta foydalansangiz, u qo'shimcha so'rovlarni yoqmaydi.

@first_my_object_time = @results.first
@user_my_object_time_matches = @first_my_object_time.user_my_object_time_matches

Agar bir xil ketma-ketlikda bo'lsangiz, to'g'ridan-to'g'ri sqldan ActiveRecord tanlash usuli sifatida:

@results = MyObjectTime.joins(:my_object,
                        "LEFT JOIN user_my_object_time_matches on my_object_times.id = user_my_object_time_matches.my_object_time_id #{additional_left_join_clause}")
                 .where( search_criteria.join(' and '), *search_values )
                 .limit(1000) 
                 .order("my_objects.day DESC, my_objects.name")
                 .paginate(:page => params[:page]).select("my_object_time.*, user_my_object_time_matches.*").as_json
1
qo'shib qo'ydi

Modellar yoki ma'lumotlar bazasi haqida to'liq ma'lumot bo'lmasa, quyida bir nechta taxminlar qilayapman. Ko'pincha sizning AR modelingizdagi tegishli uyushmalar va/yoki qamrovi bilan xom sqlni yozmasdan olishingiz mumkin:

class MyObjectTime < ApplicationRecord
  has_many :my_objects, ->(args){ where(args) }

  scope :top_1000, ->{ limit(1000) }
  scope :order_by_my_objects, ->{ order(my_objects: { day: :desc, name: :asc }) }
end

class UserMyObjectTimeMatches < ApplicationRecord
  belongs_to :my_object_time
end

MyObjectTime.my_objects(params[:search_args])
  .order_by_my_objects.top_1000
  .include(:my_object_time).paginate(page: params[:page])

Agar to'liq kodim bo'lsa, modellarni va testni sozlashim mumkin - shuning uchun bu kod testdan o'tkazilmaydi va balki tweaked bo'lishi kerak.

1
qo'shib qo'ydi

Buni shunday so'rashi mumkin emas.

Savol shuki, nima uchun bunday qilishni xohlaysiz?

Nima uchun aynan sizning munosabatingiz siz uchun qilmaydi deb o'ylaysiz?

# I already have some results pulled the way you did and I wanna use them
  @results.each do |r|
    self.foo(r, r.user_my_object_time_match);
  end

#or if I really wanted a double array, then I could do
  @results = MyObjectTime.().collect { |mot| [mot, mot.user_my_object_time_match] }

Agar ular siz uchun ishlamasa, iltimos, muammo nimani anglatishini aniqlang, chunki hozirgi vaqtda bu savol XY muammosiga o'xshaydi ( https://meta.stackoverflow.com/tags/xy-problem/info )

1
qo'shib qo'ydi
"Siz allaqachon yozgan so'rovingiz" deganingizda, mening savolimga kiritilgan "@results =" qatorini nazarda tutasizmi?
qo'shib qo'ydi muallif Dave, manba
Sizda mavjud bo'lgan ikki yondashuv bilan bog'liq muammo, yangi bir SQL so'rovi loopning har bir iteratsiyasi uchun yaratiladi. Shunday qilib, @resultsda 10000 satr mavjud bo'lsa, nima qilayotganingizni JBga nisbatan 10001 ta so'rovlar kelib chiqadi va men buni faqatgina bitta cheklashni va barcha ma'lumotlarni birdaniga olishni afzal ko'raman.
qo'shib qo'ydi muallif Dave, manba
Buning ma'nosi mantiqqa to'g'ri keladi, lekin buni amalga oshirish uchun standartlashtirilgan usul yo'q deb o'ylamayman. Albatta siz o'zingizning modellaringizni o'zingiz va siz allaqachon yozgan so'rovingizdan olingan ma'lumotlar bilan to'ldirishingiz mumkin, ammo men bu ishni yanada samaraliroq yoki to'g'riligiga ishonchim komil emas.
qo'shib qo'ydi muallif Jacob, manba