7
votes

A beaucoup de remplacement 'Finder_SQL' dans Rails 4.2

J'ai une association qui a besoin de quelques jointures / requêtes personnalisées. Lorsque vous essayez de déterminer comment implémenter cela, la réponse répétée est Finder_SQL CODE>. Cependant, dans Rails 4.2 (et plus):

ArgumentError: Touche inconnue :: Finder_SQL P> blockQuote>

Ma requête pour faire la jointure ressemble à ceci: p> xxx pré>

Je comprends que cela peut être atteint via: P>

has_many :tags, -> (user) {
  select('DISTINCT "tags".*')
    .joins('JOIN "articles_tags" ON "articles_tags"."tag_id" = "tags"."id"')
    .joins('JOIN "articles" ON "article_tags"."article_id" = "articles"."id"')
    .where('"articles"."user_id" = ?', user.id)
}, class_name: "Tag"


2 commentaires

Où écrivez-vous ce has_many: tags, -> (utilisateur) ... ? Dans user.rb?


@sajan correct - dans user.rb .


6 Réponses :


0
votes

Donc, mon hypothèse est que vous obtenez l'erreur lorsque vous essayez d'accéder à @ user.tags puisque vous avez cette association à l'intérieur du user.rb . .

Je pense donc que ce qui se passe est quand nous essayons d'accéder à l'utilisateur @ user.tags , nous essayons d'extraire les balises de l'utilisateur et à ce rails recherchera Tags dont user_id correspond à l'ID d'utilisateur actuellement fourni. Puisque les rails prennent le nom de l'association sous forme modelname_id Format par défaut, même si vous n'avez pas user_id Il tentera de rechercher dans cette colonne et de rechercher (ou d'ajouter Où "tags". "User_id" ), que vous le souhaitez ou non depuis l'objectif ultime, c'est de trouver des balises qui appartiennent à l'utilisateur actuel.

Bien sûr, ma réponse peut ne pas l'expliquer à 100%. N'hésitez pas à commenter votre pensée ou si vous trouvez quelque chose de mal, faites le moi savoir.


1 commentaires

C'est correct - j'utilise user.tags . Aussi correct que la sortie correspond à ce que vous avez dit. Avez-vous une réponse que vous pensez résoudre? Je cherche vraiment un remplacement pour le Finder_sql (avec Finder_SQL Ceci peut être effectué). Merci.



0
votes

Réponse courte forte>

OK, si je comprends bien cela correctement, je pense que j'ai la solution, qui utilise simplement les utilitaires Core Aciverecord et n'utilise pas Finder_SQL. P>

pourrait potentiellement utiliser: p> xxx pré>

ou alternativement, dans le modèle utilisateur modifie les balises HAS_MANY à P>

  Tag Load (0.4ms)  SELECT DISTINCT `tags`.* FROM `tags` INNER JOIN `articles_tags` ON `tags`.`id` = `articles_tags`.`tag_id` INNER JOIN `articles` ON `articles_tags`.`article_id` = `articles`.`id` WHERE `articles`.`user_id` = 3
 => #<ActiveRecord::AssociationRelation [#<Tag id: 3, name: "tag 3", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 4, name: "tag 4", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 5, name: "tag 5", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 6, name: "tag 6", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 7, name: "tag 7", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">]> 


5 commentaires

Essayez user.ProLoad (: tags) .ach {| utilisateur | met user.tags.inspect} et vous verrez les mauvaises requêtes de cardinalité.


Vous ne pensez pas que cela corrige le problème - voyez-vous le massif massif si vous exécutez l'extrait collé ci-dessus?


ah tu viens de déplacer les butballs xd .. j'aurai un look rapide


Oui, désolé n'a pas réalisé que cela n'allait pas clairement. Je cherche un remplacement / équivalence pour Finder_SQL qui fonctionne dans plusieurs cas - et les requêtes originales ont montré ce que je vois.


Le plus proche que je reçois, c'est utiliser has_many: tags, -> {distinct}, à travers :: articles



3
votes

Bien que cela ne corrige pas votre problème directement - si vous n'avez besoin que d'un sous-ensemble de vos données, vous pouvez potentiellement le précharger via un sous-sélection:

CREATE OR REPLACE VIEW tags_users AS (
  SELECT 
    "users"."id" AS "user_id", 
    "tags"."id" AS "tag_id"
  FROM "users"
    JOIN "articles" ON "users"."id" = "articles"."user_id"
    JOIN "articles_tags" ON "articles"."id" = "articles_tags"."article_id"
    JOIN "tags" ON "articles_tags"."tag_id" = "tags"."id"
  GROUP BY "user_id", "tag_id"
)


1 commentaires

La vue semble fonctionner (bien qu'elle soit réadonnée - mais cela a du sens). Il ne vidose pas mon schéma (probablement une question distincte) et on ne semble rien changer (mais cela soulève une exception lors de la tentative d'attribution). Cela pourrait être le meilleur effort (bien qu'un peu ennuyeux nécessitant une vue).



0
votes

Peut être utile d'utiliser Ever_load " pour forcer Activerecord Exécutez des jointures. Ça fonctionne comme inclut (: balises) .References (: balises)

Voici un code de code: xxx

l 'utilisateurs est une relation activeCord.

Ce code va toucher une base de données au moins deux fois:

  1. Sélectionnez uniquement les identifiants des utilisateurs (espérons-le, pas trop nombreux)
  2. Sélectionnez les utilisateurs avec des balises jointures via Article_Tags en évitant

    Sélectionnez * à partir de l'article_Tags où l'article_id in (1,2,3 ...) - Beaucoup


0 commentaires

0
votes

Vous êtes sur le bon chemin avec has_many: tags, via :: Articles CODE> (ou même mieux has_many: tags, -> {distinct}, via :: articles code> Comme Kevin le suggère). Mais vous devriez lire un peu sur Inclut VS Preload vs Ever_load . Vous faites ceci: xxx pré>

mais vous devez le faire: p> xxx pré>

ou ceci: p> xxx Pre>

Quand je fais que je reçois cette requête: p> xxx pré>

mais cela va toujours envoyer beaucoup de choses redondantes de la base de données à votre application. Cela sera plus rapide: p> xxx pré>

donnant: p> xxx pré>

faire juste user.first.tags.map & : Nom code> me rejoint aussi: P>

SELECT  DISTINCT "tags".*
FROM    "tags"
INNER JOIN "articles_tags"
ON      "tags"."id" = "articles_tags"."tag_id"
INNER JOIN "articles"
ON      "articles_tags"."article_id" = "articles"."id"
WHERE   "articles"."user_id" = ?


0 commentaires

0
votes

Il existe trois solutions possibles:

1) Continuez à utiliser has_many code> associations p>

faux user_id code> en l'ajoutant aux colonnes sélectionnées . p> xxx pré>

2) ajoutez une méthode d'instance sur la classe d'utilisateur p>

si vous utilisez des balises code> pour les requêtes seulement et vous Je ne l'ai pas utilisé dans des jointures, vous pouvez utiliser cette approche: p> xxx pré>

maintenant user.tags code> se comporte comme une association à des fins pratiques. p>

3) OTOH, en utilisant existe code> pourrait être performant que d'utiliser des P> distincts

  class User < ActiveRecord::Base
    def tags
      exists_sql = %Q{
        SELECT  1
        FROM    articles,
                articles_tags
        WHERE   "articles"."user_id" = #{id} AND
                "articles_tags"."article_id" = "article"."id" AND
                "articles_tags"."tag_id" = "tags.id"
      }
      Tag.where(%Q{ EXISTS ( #{exists_sql} ) })
    end
  end


0 commentaires