Как объединить несколько репозиториев с Ecto? - PullRequest
0 голосов
/ 19 февраля 2020

У меня есть две базы данных, основные базы данных (PostgreSQL) + база данных статистики (ClickHouse). База данных статистики содержит часть данных из основной базы данных, которой достаточно для выполнения расчетов. Все идентификаторы одинаковы (:binary_id) для обеих баз данных. Мне нужно найти способ объединения результатов, полученных из базы данных statisti c, с запросом к основной базе данных. С точки зрения чистого SQL решения это может быть что-то вроде этого, где VALUES - данные, полученные из базы данных статистики:

SELECT p0."id",
       p0."name",
       f1."average_count"
FROM "persons" AS p0
         JOIN (VALUES (0.0, '906af2c0-cde2-4996-9a98-bdbf986fe687'::uuid),
                      (0.2857142857142857, 'aba7c694-3453-4a55-aab9-4b542dbb4ba9'::uuid),
                      (0.2857142857142857, '2dab3350-6149-4752-a55e-7477a6ad0dd3'::uuid))
               as f1 (average_count, user_id)
              on f1.user_id = p0.id;

Мой проект активно использует Ecto и имеет много оперативных данных. построенные запросы. Вот почему я не могу просто выполнить чистые SQL запросы, как я публикую выше, и должен иметь решение на основе Ecto. Есть ли способ сделать такое объединение с Ecto?

1 Ответ

1 голос
/ 20 февраля 2020

Это не красиво, но вы можете воспользоваться Postgres 'UNNEST:

users = [
  %{id: "906af2c0-cde2-4996-9a98-bdbf986fe687", average_count: 0.0},
  %{id: "aba7c694-3453-4a55-aab9-4b542dbb4ba9", average_count: 0.2857142857142857},
  %{id: "2dab3350-6149-4752-a55e-7477a6ad0dd3", average_count: 0.2857142857142857}
]

{ids, average_counts} =
  users
  |> Stream.map(&{&1.id, &1.average_count})
  |> Enum.unzip()

dumped_ids =
  for id <- ids do
    {:ok, dumped} = Ecto.UUID.dump(id)
    dumped
  end

query =
  from p in Person,
    join: f in fragment("SELECT UNNEST(?::uuid[]) AS user_id, UNNEST(?::float[]) AS average_count", ^dumped_ids, ^average_counts),
    on: f.user_id == p.id,
    select: %{id: p.id, name: p.name, average_count: f.average_count}

Repo.all(query)

Возможно, это не лучший способ сделать это. Я не эксперт по БД. Но это работает для меня в IEx.

...