Dealing with Legacy Databases

August 23, 2016

Ergast.com is a webservice which provides a database of Historical F1 Data, starting from the 1950 season until today.

They also kindly provide a mysql database image for download. This database is the source of this toy project I’ve been working on: the idea of which is provide a {json:api} compliant API as a way for me to practice working with Elixir and Phoenix.

Challenges

Unfortunately, the mysql database image uses a non standard primary key for each table.

Here’s a sample of the races table.

raceId year round circuitId name date time url
1 2009 1 1 Australian Grand Prix 2009-03-29 06:00:00 http://en.wikipedia.org/wiki/2009_Australian_Grand_Prix
2 2009 2 2 Malaysian Grand Prix 2009-04-05 09:00:00 http://en.wikipedia.org/wiki/2009_Malaysian_Grand_Prix
3 2009 3 17 Chinese Grand Prix 2009-04-19 07:00:00 http://en.wikipedia.org/wiki/2009_Chinese_Grand_Prix
4 2009 4 3 Bahrain Grand Prix 2009-04-26 12:00:00 http://en.wikipedia.org/wiki/2009_Bahrain_Grand_Prix

Notice that the primary key for this table is raceId, and the association to the seasons table is year.

Setting Custom Primary Keys

Using the model generator in Phoenix, you’ll end up with something like this:

//web/models/race.ex

schema "races" do
  field :round, :integer
  field :name, :string
  field :url, :string
  field :date, Ecto.Date
  field :time, Ecto.Time
  timestamps()

  belongs_to :circuit, PhoenixF1JsonApi.Circuit
  belongs_to :season, PhoenixF1JsonApi.Season
end

However, this will not work, because this assumes the primary key for the race table is id, and the associations are more sane, such as season_id and circuit_id.

In order to set it up correct, I had to do the following:

//web/models/race.ex

@primary_key {:raceId, :integer, []}
@derive {Phoenix.Param, key: :raceId}
schema "races" do
  field :round, :integer
  field :name, :string
  field :date, Ecto.Date
  field :time, Ecto.Time
  field :url, :string

  belongs_to :circuit, PhoenixF1JsonApi.Circuit, foreign_key: :circuitId
  belongs_to :season, PhoenixF1JsonApi.Season, foreign_key: :year
end

Let’s break down each step.

@primary_key {:raceId, :integer, []}

Here I’m defining the primary key for this schema as raceId, and it’s an intger.

@derive {Phoenix.Param, key: :raceId}

Next, I set the @derive to use raceID as the param key. This is the ID that’s going to be used in the routes.

belongs_to :circuit, PhoenixF1JsonApi.Circuit, foreign_key: :circuitId
belongs_to :season, PhoenixF1JsonApi.Season, foreign_key: :year

Next, I had to asscoiate the foreign_keys for the circuit and season tables to those table’s primary key.

timestamps()

Finally I had to remove the timestamps() line because the data doesn’t have updated_at/created_at.

Conclusion

While it’s not difficult to use non standard keys in Phoenix, It’s really not preferred. If you have the ability to reformat the data, I highly recommend it. If not, there are ways to deal with this problem.

The main takaway for me is that the phoenix framework guides are very good. I followed the instructions here: Ecto Custom Primary Keys

Open Source

See my project on Github.


Tags:
comments powered by Disqus