简体   繁体   English

Rails ActiveRecord 查询其中关系不存在基于第三个属性

[英]Rails ActiveRecord query where relationship does not exist based on third attribute

I have an Adventure model, which is a join table between a Destination and a User (and has additional attributes such as zipcode and time_limit ).我有一个Adventure模型,它是DestinationUser之间的连接表(并且具有其他属性,例如zipcodetime_limit )。 I want to create a query that will return me all the Destinations where an Adventure between that Destination and the User currently trying to create an Adventure does not exist.我想创建一个query ,将返回我所有的Destinations ,其中的Adventure是与DestinationUser目前正试图创建一个Adventure不存在。

The way the app works when a User clicks to start a new Adventure it will create that Adventure with the user_id being that User 's id and then runs a method to provide a random Destination , ex:应用程序工作时的方式User点击开始新的Adventure ,将创建一个Adventureuser_id在于Userid ,然后运行方法来提供一个随机Destination ,例如:

Adventure.create(user_id: current_user.id) (it is actually doing current_user.adventures.new ) but same thing Adventure.create(user_id: current_user.id) (它实际上是在做current_user.adventures.new )但同样的事情

I have tried a few things from writing raw SQL queries to using .joins .我尝试了一些方法,从编写原始SQL queries到使用.joins Here are a few examples:这里有一些例子:

Destination.joins(:adventures).where.not('adventures.user_id != ?'), user.id)

Destination.joins('LEFT OUTER JOIN adventure ON destination.id = adventure.destination_id').where('adventure.user_id != ?', user.id)

The below should return all destinations that user has not yet visited in any of his adventures:下面应该返回用户在他的任何冒险中还没有访问过的所有目的地:

destinations = Destination.where('id NOT IN (SELECT destination_id FROM adventures WHERE user_id = ?)', user.id)

To select a random one append one of:要随机选择一个附加以下之一:

.all.sample
# or
.pluck(:id).sample

Depending on whether you want a full record or just id.取决于您是想要完整记录还是只是 id。

不需要加入,这应该做:

Destination.where(['id not in ?', user.adventures.pluck(:destination_id)])

In your first attempt, I see the problem to be in the usage of equality operator with where.not .在您的第一次尝试中,我发现问题在于使用where.not的相等运算符。 In your first attempt:在您的第一次尝试中:

Destination.joins(:adventures).where.not('adventures.user_id != ?'), user.id)

you're doing where.not('adventures.user_id != ?'), user.id) .你在做什么where.not('adventures.user_id != ?'), user.id) I understand this is just the opposite of what you want, isn't it?我知道这与您想要的正好相反,不是吗? Shouldn't you be calling it as where.not('adventures.user_id = ?', user.id) , ie with an equals = ?您不应该将其称为where.not('adventures.user_id = ?', user.id) ,即使用 equals =吗?

I think the following query would work for the requirement:我认为以下查询可以满足要求:

Destination.joins(:adventures).where.not(adventures: { user_id: user.id })

The only problem I see in your second method is the usage of destinations and adventures table in both join and where conditions.我在您的第二种方法中看到的唯一问题是在joinwhere条件中使用destinationsadventures表。 The table names should be plural.表名应该是复数。 The query should have been:查询应该是:

Destination
.joins('LEFT OUTER JOIN adventures on destinations.id = adventures.destination_id')
.where('adventures.user_id != ?', user.id)

ActiveRecord doesn't do join conditions but you can use your User destinations relation (eg a has_many :destinations, through: adventures ) as a sub select which results in a WHERE NOT IN (SELECT...) ActiveRecord 不执行连接条件,但您可以使用您的用户destinations关系(例如has_many :destinations, through: adventures )作为子选择,这会导致WHERE NOT IN (SELECT...)

The query is pretty simple to express and doesn't require using sql string shenanigans, multiple queries or pulling back temporary sets of ids:该查询表达起来非常简单,不需要使用 sql 字符串恶作剧、多个查询或拉回临时 id 集:

Destination.where.not(id: user.destinations)

If you want you can also chain the above realation with additional where terms, ordering and grouping clauses.如果您愿意,您还可以将上述实现与附加的 where 项、排序和分组子句联系起来。

I solved this problem with a mix of this answer and this other answer and came out with:我用这个答案另一个答案的混合解决了这个问题,并得出了:

destination = Destination.where
                         .not(id: Adventure.where(user: user)
                                           .pluck(:destination_id)
                         )
                         .sample

The .not(id: Adventure.where(user: user).pluck(:destination_id)) part excludes destinations present in previous adventures of the user. .not(id: Adventure.where(user: user).pluck(:destination_id))部分排除了用户之前冒险中出现的目的地。

The .sample part will pick a random destination from the results. .sample部分将从结果中随机选择一个目的地。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM