Article le mieux classé des associations

Je commencerai par décrire mes associations. J'ai 6 ressources. Office National, Programme, Village, Discussion, Conversation et Changement. Un NationalOffice a beaucoup de Programmes . Un programme a beaucoup Villages . Un Village a beaucoup de Discussions . Une Discussion a beaucoup de Conversations . Une conversation a un (appartient à) un changement .

Dans chaque conversation on parle d'un changement . La conversation donne alors un changement au rang. C'est le schéma de la table:

create_table "conversations", force: true do |t|
  t.integer  "discussion_id"
  t.integer  "change_id"
  t.integer  "rank" # 1 for 1st, 2 for 2nd, 3 for 3rd, ect.
end

Ce que je veux faire est le suivant: De la classe Discussion je veux être en mesure de choisir le changement le plus important. J'ai grossièrement implémenté cela avec un assistant:

  def top_change discussion
    conversation = discussion.conversations.order(:rank).first
  # Incase there are no conversations for the discussion
    if conversation.respond_to?('change')
      conversation.change.name 
    else
      'N/A'
    end
  end

Si j'attribue ce niveau à la classe Village , j'ai un problème. Comment passer en revue toutes les discussions dans un Village et trouver le meilleur score Change ? J'aurais aussi le même problème en l'essayant sur la classe Program . Et puis la classe NationalOffice .

Cela peut être réalisable via SQL ou activerecord - je ne suis pas certain.

J'espère avoir été clair - j'ai parfois des problèmes de clarté.


Modifier:

Il a été rendu évident que je n'ai pas correctement expliqué les rangs. Je vais maintenant essayer d'expliquer comment ils fonctionnent:

Chaque Conversation a un rang. Chaque conversation concerne un changement spécifique. Donc, si une Conversation à propos de Change 1 est classée 1ère dans cette Discussion * Change * 1 va gagner un 1er. Dans une autre discussion , une conversation concernant Change 1 est classée 2e. Alors Change 1 a maintenant un 1er et un 2ème (3 points?).

2 Discussions ont chacune une Conversation qui parle de Change 2. L'une est classée 1ère l'autre 3ème. Change 2 a un 1er et un 3ème (4 points?)

Dans l'ensemble, Change 1 était le changement le plus important - il y avait moins de points (score plus élevé) que Change 2.

J'espère que c'est clair. Ceci est l'application complète sur github, juste pour le contexte.

1
Pour être votre première question sur StackOverflow, cette question est très bien formulée. Félicitations.
ajouté l'auteur Nobita, source
Basé sur votre mise à jour, vous avez besoin de quelque chose de plus avancé que juste un tri. Vous avez besoin d'une sommation et ensuite d'un tri. La réponse ressemblera à peu près à ceci: stackoverflow.com/questions/14997289/… Si j'ai plus de temps, je vous donnerai une solution complète.
ajouté l'auteur Farley Knight, source

2 Réponses

J'ai fini par travailler sur la réponse avec James Strong dans le canal #rubyonrails sur le réseau freenode irc. Voici ce que nous avons:

Dans le modèle de changement, nous avons ajouté deux actions:

def self.with_rank(load_conversation=true)
  q = select("changes.*, SUM(11 - conversations.rank) as score").group("changes.id")
  q = q.joins(:conversations) if load_conversation
  return q
end

Cela a traversé toutes les conversations associées à un changement, puis SUMMED tous les rangs (mais nous l'avons fait de sorte que plus haut était meilleur IE si le rang était 1: 1 - 11 = 10) et le mettre dans la table comme * conversations_rank *. Il rejoint ensuite la table des modifications avec la table des conversations.

def self.top_ranked(load_conversation=true)
  with_rank(load_conversation).order("score desc").limit(1)
end

Ce petit extrait suivant renvoie le changement le mieux classé. C'est assez explicite.

Dans le modèle Villages, nous avons ajouté une action qui a fusionné la requête top_rank avec les changements de ce modèle (Villages.changes).

def top_change
  changes.merge Change.top_ranked(false)
end

Il a également eu l'association suivante:

has_many :conversations, through: :discussions

Cela me permet de faire ce qui suit pour obtenir le nom du plus gros changement:

@village.top_change.name

La plus grande chose que j'ai retirée de cette expérience est de décomposer les grosses requêtes de ce genre en petits morceaux gérables. Pour cela, nous avons dit:

  1. Obtenir les changements avec les rangs en fonction des conversions (leurs scores combinés)
  2. Obtenez le meilleur classement
  3. Obtenez le plus haut rang dans un village

Merci encore à John Armstrong et à tous ceux qui ont aidé.

0
ajouté

Vous pouvez utiliser has_many: through pour y parvenir.

http://ryandeussing.com/blog/ 2013/06/12/nested-associations-and-has-many-through/

Has_many à travers va ajouter des conditions supplémentaires pour rejoindre entre 3 tables ou plus. Vous devriez donc pouvoir utiliser ceci pour toutes les classes "au-dessus" de la conversation.

Fondamentalement, vous pouvez enchaîner has_many: à travers autant de fois que vous avez besoin de trouver la meilleure conversation pour l'un des modèles associés.

Dans vos modèles:

class Discussion < AR::Base
  belongs_to :village
  has_many :conversations
end

class Conversation < AR::Base
  belongs_to :discussion
  has_one :change
end

class Village < AR::Base
  has_many :discussions
  has_many :conversations, through: :discussions
end

class Programme < AR::Base
  has_many :villages
  has_many :conversations, though: :villages
end

Dans votre contrôleur:

class VillageController < ApplicationController
  def top_change
    village = Village.find(params[:id])
    @change = village.conversations.order(:rank).first.change
  end
end

class ProgrammesController < ApplicationController
  def top_change
    programme = Programme.find(params[:id])
    @change   = programme.conversations.order(:rank).first.change
  end      
end
0
ajouté
Seriez-vous capable d'expliquer ce que vous avez fait? J'ai du mal à le comprendre.
ajouté l'auteur Jragon, source
En outre, les modifications n'ont pas la colonne rank. Seules les conversations le font.
ajouté l'auteur Jragon, source
Hmm. En regardant cela, je ne suis pas sûr à 100% que je comprends comment cela va fonctionner. J'ai peut-être échoué à me faire comprendre: chaque village a plus d'une discussion. Donc, si je devais faire programme.conversations.order (: rank) ne renverrait-il pas une liste de conversations avec plusieurs 1sts et plusieurs 2ndes?
ajouté l'auteur Jragon, source
Dans les modifications, j'espère avoir expliqué un peu plus sur les changements.
ajouté l'auteur Jragon, source
Je n'ai pas entièrement lu la question avant de répondre. Je l'édite au fur et à mesure que je le lis plus lentement.
ajouté l'auteur Farley Knight, source
Eh bien, je suppose que ce serait le cas. C'est probablement un problème au niveau de l'application, car je ne comprends pas très bien comment vos rangs sont répartis entre les conversations. Peut-être ajouter plus à votre question à ce sujet?
ajouté l'auteur Farley Knight, source