8
votes

I have a database model like this:

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

What I want is query all posts for a specific user which he hasn't voted for already.

I tried it like this:

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

where 1 is just an example user id.

The problem is that this query seems to fetch all posts which have at least one vote where the user_id is not 1.
But because more than one user will vote for these posts, once there are more than one vote, all users will receive all Posts right now.

I don't know if joins is the right approach but in English my Query would be:

Give me all Posts where none of the votes have a user_id of 1

Is it possible to fetch just the posts for a user which he hasn't voted for already?

EDIT:

My database schema of the above three tables:

Votes:

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");

Posts:

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");

Users:

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);
2
Could you please post the schema of the above three tables?1000111
@1000111 What do you mean? The first few lines show the relations between the tables. What else do you need?Ybrin
paste the output of this query : show create table <tableName>1000111
@1000111 I edited my questionYbrin

2 Answers

8
votes

So if you don't care about ignoring the posts that are created by the user, it should be something like this

Find out which posts the user voted first, then the not voted ones.

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

I set up an example like what you described. I created one user, id '1', and five posts, ids '1' - '5'. Then I created likes for posts '2' and '4'. The syntax in the other answer did not work, it complained about the not. The following works to select the posts (1, 3, 5) that have not been liked by user '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}>]