For my hobby project, I’m writing software to analyze a board game, working with a large dataset of recorded online games. I’m using Postgres, have a ‘games’ table, and am storing the moves of each game in a ‘moves’ field. Each move encodes to a short string, so the ‘moves’ field is just an array of strings.
Here’s my migration:
def change
create table(:games) do
# other fields omitted
add :moves, {:array, :string}
end
create index(:records, [:moves], using: "GIN")
end
And here’s my schema:
schema "games" do
# other fields omitted
field :moves, {:array, :string}
end
My issue - I’m trying to fetch the games where the move at ‘index’ is ‘move’. I expected something like this to work:
Repo.all(from game in Game, where: game.moves[^index] == ^move)
However, it gave me this error:
** (RuntimeError) expected field `moves` to be an embed or a map, got: `{:array, :string}`
(elixir 1.11.3) lib/enum.ex:1533: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(elixir 1.11.3) lib/enum.ex:2193: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir 1.11.3) lib/enum.ex:2193: Enum."-reduce/3-lists^foldl/2-0-"/3
When I drop down to a SQL fragment, it works just fine:
Repo.all(from game in Games, where: fragment("moves[?] = ?", ^index, ^move))
Is there a way, with Ecto, to access my array by index without having to resort to the fragment of raw SQL? I was genuinely surprised when the natural way to access an array didn’t work.
While you’re here, is this the best way to be doing this? Right now I’m working with a toy subset of the data, but the full set is over two billion games. Is the GIN index the way to go? Will it work with an array of strings, or should I encode them as integers instead? And will the index still benefit me if I’m querying using more than one field – say, all games with move[1] = ‘move’ AND with player = ‘Joe’?
Sorry for all the questions – I haven’t yet found a good resource for learning advanced Postgres.