Lorsqu'il existe une catégorie à plusieurs niveaux comme viande> poulet> viande
Après avoir enregistré la catégorie de viande dans la recette, j'ai implémenté une fonction pour compter la catégorie parent viande> poulet
également.
Je n'ai pas pu trouver un article qui ajoutait un compteur à une catégorie à plusieurs niveaux utilisant l'ascendance, alors je l'ai cherché moi-même et l'ai mis en œuvre, donc je vais l'expliquer.
Si vous avez une personne similaire, veuillez vous y référer.
Ruby: 2.6.6
Rails: 6.0.2.2
Nous créons un site pour publier des recettes.
Il existe un DB de recette et un DB de catégorie.
La recette a plusieurs catégories. (Tableau intermédiaire)
La catégorie est une catégorie à plusieurs niveaux. Exemple: viande> poulet> viande
Implémenté avec ascendance (modèle d'énumération d'itinéraire)
Un compteur est implémenté pour voir combien de recettes il y a dans une catégorie.
ancestry: 3.0.7
counter_culture: 2.5.1
Cliquez ici pour l'introduction de counter_culture. Agrégation du nombre d'enregistrements associés (cache de compteur) --Qiita
Recipes Table
Table name: recipes
id :bigint not null, primary key
title :string not null
description :string not null
Tableau intermédiaire entre la recette et la catégorie Plusieurs catégories sont enregistrées pour une recette.
Table name: recipe_categories
id :bigint not null, primary key
recipe_id :bigint not null
category_id :bigint not null
Categories Table
Table name: categories
id :bigint not null, primary key
name :string not null
ancestry :string
recipes_count :integer default(0), not null
Model
class Recipe < ApplicationRecord
has_many :recipe_categories, dependent: :destroy
end
class Category < ApplicationRecord
has_ancestry
has_many :recipe_categories, dependent: :destroy
end
class RecipeCategory < ApplicationRecord
belongs_to :recipe
belongs_to :category
counter_culture :category, column_name: :recipes_count
end
S'il s'agit d'une relation 1: 1, elle sera comptée simplement en mettant ce qui suit dans le tableau intermédiaire.
counter_culture :category, column_name: :recipes_count
Cette fois, je voudrais ajouter une fonction qui compte la catégorie parente en même temps.
Par exemple Il y a une catégorie de viande dans la recette du poulet Teruyaki.
Recipe.find(1)
=> #<Recipe
id: 1,
title: "Poulet teriyaki"
>
RecipeCategory.find(1)
=> #<RecipeCategory
id: 1,
recipe_id: 1,
category_id: 20
>
Category.find(20)
=> #<Category
id: 20,
name: "Viande de poitrine",
ancestry: "1/10",
recipes_count: 1
>
La catégorie de viande a la relation suivante.
id:1 > id:10 > id:20 Viande> Poulet> Poitrine
La "viande" associée à un compteur 1: 1 est comptée, mais pas la "viande" et le "poulet". J'ai essayé de le mettre en œuvre pour que les catégories parentales «viande» et «poulet» soient également comptées.
En conclusion, je l'ai implémenté comme ça.
class RecipeCategory < ApplicationRecord
belongs_to :recipe
belongs_to :category
counter_culture :category, column_name: :recipes_count,
foreign_key_values: proc { |category_id| Category.find(category_id).path_ids }
end
Foreign_key_values
est une option pour écraser les clés externes.
Le "category_id: 20" normalement associé devient la clé externe, et le Category id: 20 count augmente ou diminue.
Si vous passez un nombre au format tableau à Foreign_key_values
, la valeur transmise sera utilisée comme clé externe pour augmenter ou diminuer le nombre de toutes les cibles.
Puisque nous voulons compter toutes les catégories parent et enfant, nous l'implémentons en passant la valeur de «[1, 10, 20]» dans l'exemple.
J'ai évoqué la référence officielle et l'article qui a été traduit et expliqué. GitHub - magnusvk/counter_culture: Turbo-charged counter caches for your Rails app. Cache de compteur haute performance pour Rails gem'counter_culture 'README (traduction) | TechRacho
foreign_key_values: proc { |category_id| Category.find(category_id).path_ids }
proc { |category_id| }
Passez d'abord l'argument avec proc, à ce moment c'est la clé externe normale (20
dans ce cas)
Category.find(category_id)
Obtenez l'objet d'enregistrement cible.
.path_ids
Si vous faites path_ids
avec la fonction d'ascendance, vous pouvez obtenir l'ID de la relation parent-enfant de l'objet dans une liste.
Référence: [Français] Gem Ancestry Official Document --Qiita
Lorsque vous l'exécutez, vous pouvez voir que => [1, 10, 20]
est renvoyé.
Category.find(20).path_ids
Category Load (1.0ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 20], ["LIMIT", 1]]
=> [1, 10, 20]
En passant [1, 10, 20]
à Foreign_key_values:
, toutes les catégories parent-enfant peuvent maintenant être comptées.
counter_culture
a une fonction pour recalculer le nombre, counter_culture_fix_counts
.
Je l'utilise pour compter les données existantes et corriger les écarts de comptage, mais je ne peux pas utiliser cette fonctionnalité avec l'option Foreign_key_values
.
RecipeCategory.counter_culture_fix_counts
=> Fixing counter caches is not supported when using :foreign_key_values;
you may skip this relation with :skip_unsupported => true
Après avoir enquêté, lors de l'utilisation de Foreign_key_values
, il semble qu'il n'y ait pas d'autre choix que d'implémenter la fonction de recalcul par moi-même, alors j'ai essayé de l'implémenter.
Dès la conclusion, je l'ai implémenté comme ça. Puisque tout a été recalculé, cela peut prendre un certain temps s'il y a de nombreux cas. Veuillez me faire savoir s'il existe une bonne méthode de mise en œuvre.
class RecipeCategory < ApplicationRecord
#...
#Recettes de catégorie_Recalculer tous les comptes
def self.fix_counts
Category.update_all(recipes_count: 0)
target_categories = pluck(:category_id)
#Calculez le nombre de catégories=> { 1: 10, 10: 3, 20: 1 }
count_categories = target_categories.group_by(&:itself).transform_values(&:size)
count_categories.each do |category_id, count|
count_up_categories = Category.find(category_id).path_ids
Category.update_counters(count_up_categories, recipes_count: count)
end
end
end
Le contenu en cours d'exécution est le suivant.
Pour la méthode de calcul du nombre de 3, je me suis référé à l'article suivant. Comptez le nombre d'éléments identiques dans le blog array-patorash
Pour savoir comment augmenter le nombre de 5 par le nombre, reportez-vous à l'article suivant. [Rails + MySQL] Implémentation du compteur [Sinon, je veux en créer un nouveau, et s'il y en a un, je veux l'incrémenter de manière appropriée] --Qiita ActiveRecord::CounterCache::ClassMethods
Maintenant, lorsque vous exécutez RecipeCategory.fix_counts
, le décompte sera recalculé.
En définissant RecipeCategory sur ce qui suit, nous avons pu implémenter la fonction de compteur pour la catégorie parent-enfant.
class RecipeCategory < ApplicationRecord
belongs_to :recipe
belongs_to :category
counter_culture :category, column_name: :recipes_count,
foreign_key_values: proc { |category_id| Category.find(category_id).path_ids }
#Recettes de catégorie_Recalculer tous les comptes
def self.fix_counts
Category.update_all(recipes_count: 0)
target_categories = pluck(:category_id)
#Calculez le nombre de catégories=> { 100: 3, 102: 2 }
count_categories = target_categories.group_by(&:itself).transform_values(&:size)
count_categories.each do |category_id, count|
count_up_categories = Category.find(category_id).path_ids
Category.update_counters(count_up_categories, recipes_count: count)
end
end
end
Veuillez vous y référer. Si vous avez des FB ou des améliorations, veuillez nous en informer dans les commentaires.