There is a "User" model, in which there is a username field and a dependent Friend model, which contains the username friends. How to get all "not friends"?

 @user = User.find_by username: current_user.username - текущий пользователь @users = User.where.not(username: current_user.username) - все пользователи кроме текущего @friends = @user.friends.all - все друзья 

accordingly, you need to get:

 @notfriends = @users - @friends" по полю username 

I tried this:

 @notfriends = @users.where.not(username: @friends.username) 

Naturally did not work, because:

undefined method `username 'for Friend

How to formulate this subquery so that the call goes not to the object, but to all entities within it?

All that came to mind is a cycle in an action that is passed by a query on all instances of @friends by username .

 @friends.each do |friend| @notfriends = @users.where.not(username: friend.username) end 

However, I would like to do this not with such a structure, but with one request.

    2 answers 2

     @notfriends = @users.where.not(username: @friends.username) 

    Uh-oh, almost, almost! It was necessary to make only select on user names:

     @notfriends = @users.where.not(username: @friends.select(:username)) 

    A request of the form will be:

     SELECT * FROM users WHERE username NOT IN (SELECT username FROM ...) 

    The technique is called the "subquery". If you come up with a solution to SQL, this option suggests itself right away.

    ActiveRecord itself uses IN (or NOT IN ) when a value collection is specified as a value, be it an array or a subquery. It is clear that @friends ( ActiveRecord::Relation on the Friend model) does not have a username method, but consider the name of the method: the user collection has more than one username!

    • I tried your option, for some reason does not return anything. - Valentin Andreev
    • @ValentinAndreev what kind of SQL does this make? - D-side
    • SELECT "users". * FROM "users" WHERE ("users". "Username"! = $ 1) AND ("users". "Username" NOT IN (SELECT "friends". "Username" FROM "friends" WHERE "friends "." user_id "= $ 2)) [[" username "," One "], [" user_id ", 4]] - Valentin Andreev
    • @ValentinAndreev looks right: get all users whose username not equal to "One" and is not among friends.username , who have friends.user_id = 4 . Either I misunderstand your logic, or this is exactly what you need. And if it returns nothing, then you have such data. - D-side
    • The data is really crooked (some were created for checking via the console), some of the fields are empty, but @notfriends = @ users.where.not (username: @ friends.map (&: username)) - it worked as it should. It will be necessary to see when everything will be done normally. - Valentin Andreev

    In this case, you will have to use the NOT IN(...) SQL construct; operators equal to or not equal are not enough here. One option might look like this.

     @notfriends = @users .where('username NOT IN(?), @friends.username) .where.not(username: current_user.username) 

    Better @friends.username and current_user.username pre-merge into one array so that only one NOT IN(...) condition is left in the SQL query. Even better, instead of username , use id identifiers.

    • Everything was really solved through NOT_IN, I found a lot of different variants of the request: @notfriends = @ users.where.not (username: @ friends.map (&: username)) - Valentin Andreev