如何在Rails中嵌入一堆ActiveRecord对象和关系?

时间:2021-01-20 13:18:14

I am working with an API that provides bus arrival data. For every request, I get back (among other things) a list of which routes serve the stop in question. For example, if the list includes result for bus route #1, 2, and 5, then I know that those serve this stop.

我正在使用提供总线到达数据的API。对于每个请求,我都会返回(除其他外)一个列表,列出哪些路由服务于该站点。例如,如果列表包含公交路线#1,2和5的结果,那么我知道那些服务于此停止。

I have a many-to-many relationship set up between Route and Stop, and I want to dynamically check and update these associations on every request. There is no "master list" of which routes serve which stops, so this seems like the best way to get this data.

我在Route和Stop之间建立了多对多关系,我想在每个请求上动态检查和更新这些关联。没有“主列表”,哪些路由服务停止,所以这似乎是获取此数据的最佳方式。

I believe that the way I'm doing it now is very inefficient:

我相信我现在这样做的方式非常低效:

# routes is an array of [number, destination] that I build while iterating over the data
routes.uniq.each do |route|
  number      = route[0]
  destination = route[1]

  r = Route.find_by_number_and_destination(number, destination)

  if !r
    r = Route.new :number => number, :destination => destination
    r.save
  end

  # I have to check if it already exists because I can't find a way
  # to create a uniqueness constraint on the join table with 2 foreign keys
  r.stops << stop unless r.stops.include? stop
end

Basically, I have to do 2 things for every route I find: 1) Create it if it doesn't already exist, 2) Add a relationship to the current stop if it doesn't already exist.

基本上,我必须为我找到的每条路线做两件事:1)如果它还不存在则创建它2)如果当前停止不存在则添加关系。

Is there a better way to do this, for example by getting a bunch of the data in memory and doing some of the processing on the app server side, in order to avoid the multitude of database calls I'm currently doing?

有没有更好的方法来做到这一点,例如通过在内存中获取一堆数据并在应用服务器端进行一些处理,以避免我目前正在进行的大量数据库调用?

3 个解决方案

#1


1  

If I get it right, you (should) have 2 models. A Route model, and a Stop model.

如果我做对了,你(应该)有2个型号。路径模型和停止模型。

Here's how I would define these models:

以下是我如何定义这些模型:

class Route < ActiveRecord::Base
  has_and_belongs_to_many :stops
  belongs_to :stop, :foreign_key => 'destination_id'
end

class Stop < ActiveRecorde::Base
  has_and_belongs_to_many :routes
end

And here's how I would set up my tables:

以下是我如何设置表格:

create_table :routes do |t|
  t.integer :destination_id
  # Any other information you want to store about routes
end

create_table :stops do |t|
  # Any other information you want to store about stops
end

create_table :routes_stops, :primary_key => [:route_id, :stop_id] do |t|
  t.integer :route_id
  t.integer :stop_id
end

Finally, here's the code I'd use:

最后,这是我使用的代码:

# First, find all the relevant routes, just for caching.
Route.find(numbers)

r = Route.find(number)
r.destination_id = destination
r.stops << stop

This should use only a few SQL queries.

这应该只使用几个SQL查询。

#2


1  

Try this gem: https://github.com/seamusabshere/upsert

试试这个宝石:https://github.com/seamusabshere/upsert

Docs say its 80% faster than find_or_create_by

Docs说它比find_or_create_by快80%

#3


0  

There's likely a good way to cleanup the stops call, but this cleans it up quite a bit assuming I'm picturing properly how routes is structured.

可能有一种很好的方法可以清理停止呼叫,但是如果我正确地描绘了路由的结构,这可以清理它。

routes.uniq.each do |number, destination|

  r = Route.find_or_create_by_number_and_destination(route[0], destination)

  r.stops << stop unless r.stops.include? stop

end

#1


1  

If I get it right, you (should) have 2 models. A Route model, and a Stop model.

如果我做对了,你(应该)有2个型号。路径模型和停止模型。

Here's how I would define these models:

以下是我如何定义这些模型:

class Route < ActiveRecord::Base
  has_and_belongs_to_many :stops
  belongs_to :stop, :foreign_key => 'destination_id'
end

class Stop < ActiveRecorde::Base
  has_and_belongs_to_many :routes
end

And here's how I would set up my tables:

以下是我如何设置表格:

create_table :routes do |t|
  t.integer :destination_id
  # Any other information you want to store about routes
end

create_table :stops do |t|
  # Any other information you want to store about stops
end

create_table :routes_stops, :primary_key => [:route_id, :stop_id] do |t|
  t.integer :route_id
  t.integer :stop_id
end

Finally, here's the code I'd use:

最后,这是我使用的代码:

# First, find all the relevant routes, just for caching.
Route.find(numbers)

r = Route.find(number)
r.destination_id = destination
r.stops << stop

This should use only a few SQL queries.

这应该只使用几个SQL查询。

#2


1  

Try this gem: https://github.com/seamusabshere/upsert

试试这个宝石:https://github.com/seamusabshere/upsert

Docs say its 80% faster than find_or_create_by

Docs说它比find_or_create_by快80%

#3


0  

There's likely a good way to cleanup the stops call, but this cleans it up quite a bit assuming I'm picturing properly how routes is structured.

可能有一种很好的方法可以清理停止呼叫,但是如果我正确地描绘了路由的结构,这可以清理它。

routes.uniq.each do |number, destination|

  r = Route.find_or_create_by_number_and_destination(route[0], destination)

  r.stops << stop unless r.stops.include? stop

end