2016-07-17 33 views
8

Mam modelu bazy danych tak:ActiveRecord dołącza warunek spełniają wszelkie stosunki

Post 
    has_many :votes 
    belongs_to :user 
User 
    has_many :posts 
    has_many :votes 
Vote 
    belongs_to :post 
    belongs_to :user 

Co chcę jest kwerenda wszystko posts dla konkretnego użytkownika, który nie głosował za już.

Próbowałem to tak:

Post.left_outer_joins(:votes).where.not(votes: { user_id: 1 }) 

gdzie 1 jest tylko przykładem id użytkownika.

Problem polega na tym, że to zapytanie wydaje się pobrać wszystkie posts, które mają co najmniej jeden głos, gdzie user_id nie jest 1.
Ale ponieważ więcej niż jeden użytkownik będzie głosował na te posty, gdy pojawi się więcej niż jeden głos, wszyscy użytkownicy otrzymają teraz wszystkie posty.

ja nie wiem, czy joins jest to najlepsze rozwiązanie, ale w języku angielskim moje pytanie byłoby:

Daj mi wszystkie posty, w których żaden z głosów mają user_id z 1

Czy można pobrać tylko posts dla użytkownika, na który już nie głosował?

EDIT:

moim schemacie bazy danych z trzech powyższych tabelach:

głosów:

CREATE TABLE "votes" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
     "post_id" integer, 
     "user_id" integer, 
     "created_at" datetime NOT NULL, 
     "updated_at" datetime NOT NULL, 
     "vote_type" integer DEFAULT 0); 
CREATE INDEX "index_votes_on_post_id" ON "votes" ("post_id"); 
CREATE INDEX "index_votes_on_user_id" ON "votes" ("user_id"); 

postów:

CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
     "photo_gcs_key" varchar, "photo_gcs_bucket" varchar, 
     "user_id" integer, "campaign_id" integer, 
     "created_at" datetime NOT NULL, 
     "updated_at" datetime NOT NULL); 
CREATE INDEX "index_images_on_user_id" ON "images" ("user_id"); 
CREATE INDEX "index_images_on_campaign_id" ON "images" ("campaign_id"); 

użytkowników:

CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
     "uid" varchar, "display_name" varchar, "email" varchar, 
     "photo_url" varchar, "photo_gcs_key" varchar, 
     "photo_gcs_bucket" varchar, "user_name" varchar, 
     "created_at" datetime NOT NULL, 
     "updated_at" datetime NOT NULL); 
+0

Czy mógłbyś opublikować schemat powyższych trzech tabel? – 1000111

+0

@ 1000111 Co masz na myśli? Pierwsze kilka wierszy pokazuje relacje między tabelami. Co jeszcze potrzebujesz? – Ybrin

+0

wklej dane wyjściowe tego zapytania: 'pokaż utwórz tabelę ' – 1000111

Odpowiedz

8

Więc jeśli nie dbają o ignorowanie postów, które zostały utworzone przez użytkownika, powinno być coś takiego

Sprawdzaj, które posty użytkownik głosował pierwszy, wówczas nie głosowali nich.

Post.where.not(id: Vote.where(user_id: user_id).select(:post_id)) 
1

Przygotowałem przykład, jak to, co opisałeś. Stworzyłem jednego użytkownika, id '1' i pięć postów, ids '1' - "5". Potem stworzyłem polubienia dla postów "2" i "4". Składnia w drugiej odpowiedzi nie zadziałała, narzekała na not. Poniższe działa, aby wybrać posty (1, 3, 5), które nie zostały polubione przez użytkownika "1"

irb(main):062:0> Post.where(Sequel.~(id: Vote.select(:post_id).where(user_id: 1))) 
=> #<Sequel::MySQL::Dataset: "SELECT * FROM `posts` WHERE (`id` NOT IN (SELECT `post_id` FROM `votes` WHERE (`user_id` = 1)))"> 

irb(main):063:0> Post.where(Sequel.~(id: Vote.select(:post_id).where(user_id: 1))).all 
=> [#<Post @values={:id=>1, :photo_gcs_key=>nil, :photo_gcs_bucket=>nil, :user_id=>1, :campaign_id=>nil, :created_at=>2016-08-12 17:23:53 -0700, :updated_at=>2016-08-12 17:23:53 -0700}>, #<Post @values={:id=>3, :photo_gcs_key=>nil, :photo_gcs_bucket=>nil, :user_id=>1, :campaign_id=>nil, :created_at=>2016-08-12 17:23:56 -0700, :updated_at=>2016-08-12 17:23:56 -0700}>, #<Post @values={:id=>5, :photo_gcs_key=>nil, :photo_gcs_bucket=>nil, :user_id=>1, :campaign_id=>nil, :created_at=>2016-08-12 17:23:57 -0700, :updated_at=>2016-08-12 17:23:57 -0700}>] 
+0

Ups, moja odpowiedź nie jest konfiguracją ActiveRecord, jest to konfiguracja modelu Ruby Sequel. – Tony