From 8c6d232a67d88b528865a03e68d0f1251fb6b5de Mon Sep 17 00:00:00 2001 From: SimonLab Date: Wed, 6 Mar 2019 08:21:51 +0000 Subject: [PATCH 1/7] add "distinct on" in query, #40 --- lib/alog/connection.ex | 45 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/alog/connection.ex b/lib/alog/connection.ex index 496faa0..59b4209 100644 --- a/lib/alog/connection.ex +++ b/lib/alog/connection.ex @@ -27,7 +27,50 @@ defmodule Alog.Connection do defdelegate to_constraints(error_struct), to: EAPC @impl true - defdelegate all(query), to: EAPC + def all(query) do + iodata_query = EAPC.all(query) + + # sub = + # from(m in __MODULE__, + # distinct: m.entry_id, + # order_by: [desc: :updated_at], + # select: m + # ) + # + # query = from(m in subquery(sub), where: not m.deleted, select: m) + + +# SELECT +# s0."id", +# s0."name", +# s0."entry_id", +# s0."deleted", +# s0."inserted_at", +# s0."updated_at" +# FROM +# (SELECT DISTINCT ON (d0."entry_id") +# d0."id" AS "id" +# , d0."name" AS "name" +# , d0."entry_id" AS "entry_id" +# , d0."deleted" AS "deleted" +# , d0."inserted_at" AS "inserted_at" +# , d0."updated_at" AS "updated_at" +# FROM "drink_types" AS d0 +# ORDER BY d0."entry_id", d0."updated_at" DESC) +# AS s0 WHERE (NOT (s0."deleted")) + + + query = iodata_query + |> IO.iodata_to_binary() + |> distinct_entry_id() + + IO.inspect query + query + end + + defp distinct_entry_id("SELECT " <> query) do + IO.iodata_to_binary(["SELECT ", "DISTINCT ON (\"entry_id\" ) ", query]) + end @impl true defdelegate update_all(query, prefix \\ nil), to: EAPC From c934649b91b81a47b7cfe95b79d73cc208c0044c Mon Sep 17 00:00:00 2001 From: SimonLab Date: Wed, 6 Mar 2019 13:09:54 +0000 Subject: [PATCH 2/7] use regex instead of String.split to match query terms, https://github.com/dwyl/alog/issues/40#issuecomment-470015373 --- config/test.exs | 4 ++-- lib/alog/connection.ex | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/config/test.exs b/config/test.exs index cead70a..47d1f1b 100644 --- a/config/test.exs +++ b/config/test.exs @@ -2,8 +2,8 @@ use Mix.Config config :alog, Alog.Repo, username: "postgres", - password: "postgres", - database: "test_app_dev", + password: "docker", + database: "routinedb", hostname: "localhost", pool: Ecto.Adapters.SQL.Sandbox, priv: "priv/repo/test_app/" diff --git a/lib/alog/connection.ex b/lib/alog/connection.ex index 59b4209..167008f 100644 --- a/lib/alog/connection.ex +++ b/lib/alog/connection.ex @@ -68,8 +68,29 @@ defmodule Alog.Connection do query end - defp distinct_entry_id("SELECT " <> query) do - IO.iodata_to_binary(["SELECT ", "DISTINCT ON (\"entry_id\" ) ", query]) + # defp distinct_entry_id("SELECT " <> fields <> " FROM " <> table_name <> " AS " <> table_as <> " " <> rest_query) do + # + # IO.iodata_to_binary(["SELECT ", "DISTINCT ON (#{table_as}\".entry_id\" ) ", query]) + # end + + defp distinct_entry_id(query) do + query_data = get_query_data(query) + if (query_data["table_name"] == "\"schema_migrations\"") do + query + else + IO.iodata_to_binary( + [ "SELECT DISTINCT ON (#{query_data["table_as"]}.\"entry_id\" ) ", + query_data["fields"], + " FROM ", + query_data["table_name"], " AS ", query_data["table_as"], + query_data["rest_query"] + ] + ) + end + end + + defp get_query_data(query) do + Regex.named_captures(~r/(\bSELECT\b)\s(?.*)\sFROM\s(?.*)\sas\s(?.*)(?.*)/i, query) end @impl true From 0eb4ce577cba06ab537c25bb47c5736fb036da37 Mon Sep 17 00:00:00 2001 From: SimonLab Date: Wed, 6 Mar 2019 13:26:11 +0000 Subject: [PATCH 3/7] order by result by entry_id and inserted_at, #40 --- config/test.exs | 4 ++-- lib/alog/connection.ex | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/test.exs b/config/test.exs index 47d1f1b..cead70a 100644 --- a/config/test.exs +++ b/config/test.exs @@ -2,8 +2,8 @@ use Mix.Config config :alog, Alog.Repo, username: "postgres", - password: "docker", - database: "routinedb", + password: "postgres", + database: "test_app_dev", hostname: "localhost", pool: Ecto.Adapters.SQL.Sandbox, priv: "priv/repo/test_app/" diff --git a/lib/alog/connection.ex b/lib/alog/connection.ex index 167008f..d711174 100644 --- a/lib/alog/connection.ex +++ b/lib/alog/connection.ex @@ -83,7 +83,8 @@ defmodule Alog.Connection do query_data["fields"], " FROM ", query_data["table_name"], " AS ", query_data["table_as"], - query_data["rest_query"] + query_data["rest_query"], + " ORDER BY #{query_data["table_as"]}.\"entry_id\", #{query_data["table_as"]}.\"inserted_at\" DESC", ] ) end From 2c4f4a261eb909916ef217a486e6a8689665a4d6 Mon Sep 17 00:00:00 2001 From: SimonLab Date: Wed, 6 Mar 2019 15:01:47 +0000 Subject: [PATCH 4/7] create subquery for all function, #40 --- lib/alog/connection.ex | 70 +++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/lib/alog/connection.ex b/lib/alog/connection.ex index d711174..c68e751 100644 --- a/lib/alog/connection.ex +++ b/lib/alog/connection.ex @@ -30,68 +30,48 @@ defmodule Alog.Connection do def all(query) do iodata_query = EAPC.all(query) - # sub = - # from(m in __MODULE__, - # distinct: m.entry_id, - # order_by: [desc: :updated_at], - # select: m - # ) - # - # query = from(m in subquery(sub), where: not m.deleted, select: m) - - -# SELECT -# s0."id", -# s0."name", -# s0."entry_id", -# s0."deleted", -# s0."inserted_at", -# s0."updated_at" -# FROM -# (SELECT DISTINCT ON (d0."entry_id") -# d0."id" AS "id" -# , d0."name" AS "name" -# , d0."entry_id" AS "entry_id" -# , d0."deleted" AS "deleted" -# , d0."inserted_at" AS "inserted_at" -# , d0."updated_at" AS "updated_at" -# FROM "drink_types" AS d0 -# ORDER BY d0."entry_id", d0."updated_at" DESC) -# AS s0 WHERE (NOT (s0."deleted")) - - query = iodata_query |> IO.iodata_to_binary() |> distinct_entry_id() - IO.inspect query query end - # defp distinct_entry_id("SELECT " <> fields <> " FROM " <> table_name <> " AS " <> table_as <> " " <> rest_query) do - # - # IO.iodata_to_binary(["SELECT ", "DISTINCT ON (#{table_as}\".entry_id\" ) ", query]) - # end - defp distinct_entry_id(query) do query_data = get_query_data(query) if (query_data["table_name"] == "\"schema_migrations\"") do query else + subquery = IO.iodata_to_binary( + [ "SELECT DISTINCT ON (#{query_data["table_as"]}.\"entry_id\" ) ", + query_data["subquery_fields"], ", #{query_data["table_as"]}.\"deleted\" AS \"delted\"", + " FROM ", + query_data["table_name"], " AS ", query_data["table_as"], + query_data["rest_query"], + " ORDER BY #{query_data["table_as"]}.\"entry_id\", #{query_data["table_as"]}.\"inserted_at\" DESC" + ] + ) + IO.iodata_to_binary( - [ "SELECT DISTINCT ON (#{query_data["table_as"]}.\"entry_id\" ) ", - query_data["fields"], - " FROM ", - query_data["table_name"], " AS ", query_data["table_as"], - query_data["rest_query"], - " ORDER BY #{query_data["table_as"]}.\"entry_id\", #{query_data["table_as"]}.\"inserted_at\" DESC", - ] - ) + ["SELECT ", query_data["field_names"], " FROM (", subquery, ") AS alogsubquery WHERE (NOT alogsubquery.\"deleted\")"] + ) + end end defp get_query_data(query) do - Regex.named_captures(~r/(\bSELECT\b)\s(?.*)\sFROM\s(?.*)\sas\s(?.*)(?.*)/i, query) + data = Regex.named_captures(~r/(\bSELECT\b)\s(?.*)\sFROM\s(?.*)\sas\s(?.*)(?.*)/i, query) + data = Map.put(data, "field_names", Regex.replace(~r/#{data["table_as"]}/, data["fields"], "alogsubquery")) + + subquery_fields = data["fields"] + |> String.split(",") + |> Enum.map(fn f -> + field_name = Regex.replace(~r/#{data["table_as"]}./, f, "") + f <> " AS #{field_name}" + end) + |> Enum.join(", ") + + Map.put(data, "subquery_fields", subquery_fields) end @impl true From abf0f46d100093c9601311bc456211e438c61a45 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 11 Mar 2019 14:35:06 +0000 Subject: [PATCH 5/7] Update lib/alog/connection.ex --- lib/alog/connection.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/alog/connection.ex b/lib/alog/connection.ex index c68e751..14e3bf6 100644 --- a/lib/alog/connection.ex +++ b/lib/alog/connection.ex @@ -44,7 +44,7 @@ defmodule Alog.Connection do else subquery = IO.iodata_to_binary( [ "SELECT DISTINCT ON (#{query_data["table_as"]}.\"entry_id\" ) ", - query_data["subquery_fields"], ", #{query_data["table_as"]}.\"deleted\" AS \"delted\"", + query_data["subquery_fields"], ", #{query_data["table_as"]}.\"deleted\" AS \"deleted\"", " FROM ", query_data["table_name"], " AS ", query_data["table_as"], query_data["rest_query"], From 53c92d15ecf6c49879254536c5d9c3070e1e19c2 Mon Sep 17 00:00:00 2001 From: SimonLab Date: Tue, 12 Mar 2019 09:24:36 +0000 Subject: [PATCH 6/7] check deleted field is necessary on subquery, #40 --- lib/alog/connection.ex | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/alog/connection.ex b/lib/alog/connection.ex index 14e3bf6..7e9e81e 100644 --- a/lib/alog/connection.ex +++ b/lib/alog/connection.ex @@ -32,19 +32,23 @@ defmodule Alog.Connection do query = iodata_query |> IO.iodata_to_binary() - |> distinct_entry_id() + |> alogify_all_query() query end - defp distinct_entry_id(query) do + defp alogify_all_query(query) do query_data = get_query_data(query) + # if all query is called during migration, some column used in the + # alogify version might not be yet defined. + # Return the "normal" query if (query_data["table_name"] == "\"schema_migrations\"") do query else subquery = IO.iodata_to_binary( [ "SELECT DISTINCT ON (#{query_data["table_as"]}.\"entry_id\" ) ", - query_data["subquery_fields"], ", #{query_data["table_as"]}.\"deleted\" AS \"deleted\"", + query_data["subquery_fields"], + get_deleted_field(query_data), " FROM ", query_data["table_name"], " AS ", query_data["table_as"], query_data["rest_query"], @@ -55,7 +59,6 @@ defmodule Alog.Connection do IO.iodata_to_binary( ["SELECT ", query_data["field_names"], " FROM (", subquery, ") AS alogsubquery WHERE (NOT alogsubquery.\"deleted\")"] ) - end end @@ -74,6 +77,14 @@ defmodule Alog.Connection do Map.put(data, "subquery_fields", subquery_fields) end + defp get_deleted_field(query_data) do + if String.contains?(query_data["subquery_fields"], "#{query_data["table_as"]}.\"deleted\"") do + "" + else + ", #{query_data["table_as"]}.\"deleted\" AS \"deleted\"" + end + end + @impl true defdelegate update_all(query, prefix \\ nil), to: EAPC From d9eb8b91be9481bb18fb0919ebcc5bc2c3549883 Mon Sep 17 00:00:00 2001 From: SimonLab Date: Tue, 12 Mar 2019 10:05:35 +0000 Subject: [PATCH 7/7] add test for all, #40 --- test/all_test.exs | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/test/all_test.exs b/test/all_test.exs index 1184943..cb8c043 100644 --- a/test/all_test.exs +++ b/test/all_test.exs @@ -1,31 +1,14 @@ defmodule AlogTest.AllTest do use Alog.TestApp.DataCase - # alias Alog.TestApp.{User, Helpers} - # - # describe "all/0:" do - # test "succeeds" do - # {:ok, _} = %User{} |> User.changeset(Helpers.user_1_params()) |> User.insert() - # {:ok, _} = %User{} |> User.changeset(Helpers.user_2_params()) |> User.insert() - # - # assert length(User.all()) == 2 - # end - # - # test "does not include old items" do - # {:ok, user} = %User{} |> User.changeset(Helpers.user_1_params()) |> User.insert() - # {:ok, _} = %User{} |> User.changeset(Helpers.user_2_params()) |> User.insert() - # {:ok, _} = user |> User.changeset(%{postcode: "W2 3EC"}) |> User.update() - # - # assert length(User.all()) == 2 - # end - # - # test "all return inserted_at original value" do - # {:ok, user} = %User{} |> User.changeset(Helpers.user_3_params()) |> User.insert() - # {:ok, user_updated} = user |> User.changeset(%{postcode: "W2 3EC"}) |> User.update() - # - # [user_all] = User.all() - # assert user_all.inserted_at == user.inserted_at - # assert user_all.postcode == user_updated.postcode - # end - # end + alias Alog.TestApp.{Comment} + + describe "all/0:" do + test "succeeds" do + {:ok, _comment} = %Comment{comment: "Hello Rob"} |> Repo.insert() + {:ok, _} = %Comment{comment: "Hello Dan"} |> Repo.insert() + + assert length(Repo.all(Comment)) == 2 + end + end end