Избегайте генерации N+1 запросов при извлечении графа объектов через ActiveRecord

У меня есть две модели: AssetItem и Title.

  • AssetItem принадлежит Заголовку / Заголовок имеет много asset_items.
  • Название может иметь родительское название (тем самым создавая иерархию); Эти родительские / дочерние отношения в Titles управляются жемчужиной предков.

У меня есть контроллер, который загружает кучу AssetItems. Эти AssetItems должны поставляться с их Заголовком, родителем этого Заголовка, родителем этого Заголовка и так далее. В конце концов, контроллер представляет этот граф объектов в формате JSON. Я пытаюсь найти способ загружать всю иерархию заголовков для данного элемента ресурса.

Я делаю это в моем контроллере:

def show
  asset_items = AssetItem.includes(:title).where(:id => list_of_ids)
  eager_load_titles_for(asset_items)
  respond_with asset_items, serializer: AssetItemSerializer
end

private

def eager_load_titles_for(asset_items)
  title_ids = asset_items.map { |a| a.title.ancestor_ids }.flatten
  Title.find(title_ids) # these titles are not really used, this is just to eager load them
end

Идея состоит в том, что коллекция элементов ресурса выбирается, а затем идентификаторы заголовка, связанные с этими элементами ресурса, просто загружаются (но не используются), поэтому, когда сериализатор делает что-то вроде object.parent.parent.parent.some_attribute, он получает этот заголовок из кеша, а не дб.

Однако результаты в журнале не соответствуют моим ожиданиям:

10:19:27 web.1    |   AssetItem Load (1.3ms)  SELECT "asset_items".* FROM "asset_items" WHERE "asset_items"."id" IN (291, 311, 316, 309, 323, 304, 296, 289, 328, 284, 373, 342, 330, 335, 361, 366, 354, 347, 359, 380, 400, 405, 392, 378, 397, 417, 412, 385, 436, 429, 450, 448, 431, 455, 462, 424, 443, 481, 498, 467, 501, 493, 474, 506, 486, 479, 532, 525, 520, 513)
10:19:27 web.1    |   Title Load (0.6ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" IN (167, 182, 188, 203, 227, 242, 248, 263, 284, 299, 305, 320, 341, 356, 377, 392, 398, 413, 434, 449, 455, 470, 491, 506, 515, 530, 551, 566, 587, 602, 608, 623, 644, 659, 665, 680, 701, 716, 737, 752, 758, 773, 794, 809, 818, 833, 854, 875, 890, 911)
10:19:27 web.1    |   Title Load (0.9ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" IN (186, 187, 246, 247, 261, 262, 240, 241, 282, 283, 225, 226, 201, 202, 180, 181, 297, 298, 165, 166, 432, 433, 339, 340, 303, 304, 318, 319, 396, 397, 411, 412, 375, 376, 354, 355, 390, 391, 453, 454, 513, 514, 528, 529, 489, 490, 447, 448, 504, 505, 564, 565, 549, 550, 468, 469, 621, 622, 600, 601, 663, 664, 657, 658, 606, 607, 678, 679, 699, 700, 585, 586, 642, 643, 756, 757, 807, 808, 714, 715, 816, 817, 792, 793, 735, 736, 831, 832, 771, 772, 750, 751, 909, 910, 888, 889, 873, 874, 852, 853)
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "asset_items".* FROM "asset_items" WHERE "asset_items"."id" IN (291, 311, 316, 309, 323, 304, 296, 289, 328, 284, 373, 342, 330, 335, 361, 366, 354, 347, 359, 380, 400, 405, 392, 378, 397, 417, 412, 385, 436, 429, 450, 448, 431, 455, 462, 424, 443, 481, 498, 467, 501, 493, 474, 506, 486, 479, 532, 525, 520, 513)
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" IN (167, 182, 188, 203, 227, 242, 248, 263, 284, 299, 305, 320, 341, 356, 377, 392, 398, 413, 434, 449, 455, 470, 491, 506, 515, 530, 551, 566, 587, 602, 608, 623, 644, 659, 665, 680, 701, 716, 737, 752, 758, 773, 794, 809, 818, 833, 854, 875, 890, 911)
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" IN (186, 187, 246, 247, 261, 262, 240, 241, 282, 283, 225, 226, 201, 202, 180, 181, 297, 298, 165, 166, 432, 433, 339, 340, 303, 304, 318, 319, 396, 397, 411, 412, 375, 376, 354, 355, 390, 391, 453, 454, 513, 514, 528, 529, 489, 490, 447, 448, 504, 505, 564, 565, 549, 550, 468, 469, 621, 622, 600, 601, 663, 664, 657, 658, 606, 607, 678, 679, 699, 700, 585, 586, 642, 643, 756, 757, 807, 808, 714, 715, 816, 817, 792, 793, 735, 736, 831, 832, 771, 772, 750, 751, 909, 910, 888, 889, 873, 874, 852, 853)
10:19:27 web.1    |   Title Load (0.4ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 186]]
10:19:27 web.1    |   Title Load (0.2ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]
10:19:27 web.1    |   CACHE (0.0ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]

Как вы можете видеть, изначально идентификатор заголовка 186 и 187 загружаются в предложении where здесь:

10:19:27 web.1    |   Title Load (0.9ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" IN (186, 187, 246, 247, 261, 262, 240, 241, 282, 283, 225, 226, 201, 202, 180, 181, 297, 298, 165, 166, 432, 433, 339, 340, 303, 304, 318, 319, 396, 397, 411, 412, 375, 376, 354, 355, 390, 391, 453, 454, 513, 514, 528, 529, 489, 490, 447, 448, 504, 505, 564, 565, 549, 550, 468, 469, 621, 622, 600, 601, 663, 664, 657, 658, 606, 607, 678, 679, 699, 700, 585, 586, 642, 643, 756, 757, 807, 808, 714, 715, 816, 817, 792, 793, 735, 736, 831, 832, 771, 772, 750, 751, 909, 910, 888, 889, 873, 874, 852, 853)

а затем под этим мы снова видим:

10:19:27 web.1    |   Title Load (0.4ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 186]]
10:19:27 web.1    |   Title Load (0.2ms)  SELECT "titles".* FROM "titles" WHERE "titles"."id" = $1 LIMIT 1  [["id", 187]]

И именно поэтому мне интересно, что я делаю не так. Любая помощь будет принята с благодарностью.

0 ответов

Другие вопросы по тегам