From b1bd1639aedc405315b1960dad02bf92334bf583 Mon Sep 17 00:00:00 2001 From: exAspArk Date: Thu, 17 Jan 2019 12:05:47 -0500 Subject: [PATCH 1/2] Fix finding values for limit and offset with where arrays, close #170 --- lib/mongo_ecto/normalized_query.ex | 30 ++++++++++++++++++++++++++---- test/mongo_ecto_test.exs | 12 ++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/mongo_ecto/normalized_query.ex b/lib/mongo_ecto/normalized_query.ex index 6d20576..6b60a61 100644 --- a/lib/mongo_ecto/normalized_query.ex +++ b/lib/mongo_ecto/normalized_query.ex @@ -345,6 +345,22 @@ defmodule Mongo.Ecto.NormalizedQuery do defp offset_limit(nil, _params, _pk, _query, _where), do: nil + defp offset_limit( + %Query.QueryExpr{expr: {:^, l, [idx]} = expr}, + params, + pk, + %Query{wheres: wheres} = query, + where + ) do + param_offset = + Enum.reduce(wheres, 0, fn %Query.BooleanExpr{expr: expr}, acc -> + _from..to = pair_params_range(expr) + acc + to + end) + + value({:^, l, [idx + param_offset]}, params, pk, query, where) + end + defp offset_limit(%Query.QueryExpr{expr: expr}, params, pk, query, where), do: value(expr, params, pk, query, where) @@ -466,9 +482,10 @@ defmodule Mongo.Ecto.NormalizedQuery do {field(left, pk, query, place), ["$in": []]} end - defp pair({:in, _, [left, {:^, _, [ix, len]}]}, params, pk, query, place) do + defp pair({:in, _, [left, _]} = expr, params, pk, query, place) do args = - ix..(ix + len - 1) + expr + |> pair_params_range() |> Enum.map(&elem(params, &1)) |> Enum.map(&value(&1, params, pk, query, place)) @@ -483,9 +500,10 @@ defmodule Mongo.Ecto.NormalizedQuery do {field(left, pk, query, place), [{binary_op(op), value(right, params, pk, query, place)}]} end - defp pair({:not, _, [{:in, _, [left, {:^, _, [ix, len]}]}]}, params, pk, query, place) do + defp pair({:not, _, [{:in, _, [left, _]}]} = expr, params, pk, query, place) do args = - ix..(ix + len - 1) + expr + |> pair_params_range() |> Enum.map(&elem(params, &1)) |> Enum.map(&value(&1, params, pk, query, place)) @@ -545,4 +563,8 @@ defmodule Mongo.Ecto.NormalizedQuery do defp error(place) do raise ArgumentError, "Invalid expression for MongoDB adapter in #{place}" end + + defp pair_params_range({:in, _, [_, {:^, _, [ix, len]}]}), do: ix..(ix + len - 1) + defp pair_params_range({:not, _, [{:in, _, [_, {:^, _, [ix, len]}]}]}), do: ix..(ix + len - 1) + defp pair_params_range(expr), do: 0..0 end diff --git a/test/mongo_ecto_test.exs b/test/mongo_ecto_test.exs index 28cf4bf..9cf65f0 100644 --- a/test/mongo_ecto_test.exs +++ b/test/mongo_ecto_test.exs @@ -91,6 +91,18 @@ defmodule Mongo.EctoTest do assert 10 == TestRepo.one(query) end + test "where in ids + dynamic limit + dynamic offset" do + post1 = TestRepo.insert!(%Post{}) + post2 = TestRepo.insert!(%Post{}) + post3 = TestRepo.insert!(%Post{}) + ids = [post1.id, post2.id, post3.id] + limit = 1 + offset = 1 + + query = from p in Post, where: p.id in ^ids, limit: ^limit, offset: ^offset + assert TestRepo.all(query) == [post2] + end + # test "partial update in map" do # post = TestRepo.insert!(%Post{meta: %{author: %{name: "michal"}, other: "value"}}) # TestRepo.update_all(Post, set: [meta: change_map("author.name", "michal")]) From 2d195a33e4c2519b51ba37d003e836a72a880194 Mon Sep 17 00:00:00 2001 From: exAspArk Date: Fri, 18 Jan 2019 13:23:12 -0500 Subject: [PATCH 2/2] Make it unsolvable --- lib/mongo_ecto/normalized_query.ex | 49 ++++++++++++++++++++++-------- test/mongo_ecto_test.exs | 21 +++++++++---- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/lib/mongo_ecto/normalized_query.ex b/lib/mongo_ecto/normalized_query.ex index 6b60a61..32aa9e4 100644 --- a/lib/mongo_ecto/normalized_query.ex +++ b/lib/mongo_ecto/normalized_query.ex @@ -346,19 +346,25 @@ defmodule Mongo.Ecto.NormalizedQuery do defp offset_limit(nil, _params, _pk, _query, _where), do: nil defp offset_limit( - %Query.QueryExpr{expr: {:^, l, [idx]} = expr}, + %Query.QueryExpr{expr: {:^, l, [idx]}}, params, pk, %Query{wheres: wheres} = query, - where + "limit clause" = where ) do - param_offset = - Enum.reduce(wheres, 0, fn %Query.BooleanExpr{expr: expr}, acc -> - _from..to = pair_params_range(expr) - acc + to - end) + where_params_offset = where_params_offset(wheres) + value({:^, l, [where_params_offset + 1]}, params, pk, query, where) + end - value({:^, l, [idx + param_offset]}, params, pk, query, where) + defp offset_limit( + %Query.QueryExpr{expr: {:^, l, [idx]}}, + params, + pk, + %Query{wheres: wheres} = query, + "offset clause" = where + ) do + where_params_offset = where_params_offset(wheres) + value({:^, l, [where_params_offset + 2]}, params, pk, query, where) end defp offset_limit(%Query.QueryExpr{expr: expr}, params, pk, query, where), @@ -485,7 +491,7 @@ defmodule Mongo.Ecto.NormalizedQuery do defp pair({:in, _, [left, _]} = expr, params, pk, query, place) do args = expr - |> pair_params_range() + |> where_params_range() |> Enum.map(&elem(params, &1)) |> Enum.map(&value(&1, params, pk, query, place)) @@ -503,7 +509,7 @@ defmodule Mongo.Ecto.NormalizedQuery do defp pair({:not, _, [{:in, _, [left, _]}]} = expr, params, pk, query, place) do args = expr - |> pair_params_range() + |> where_params_range() |> Enum.map(&elem(params, &1)) |> Enum.map(&value(&1, params, pk, query, place)) @@ -564,7 +570,24 @@ defmodule Mongo.Ecto.NormalizedQuery do raise ArgumentError, "Invalid expression for MongoDB adapter in #{place}" end - defp pair_params_range({:in, _, [_, {:^, _, [ix, len]}]}), do: ix..(ix + len - 1) - defp pair_params_range({:not, _, [{:in, _, [_, {:^, _, [ix, len]}]}]}), do: ix..(ix + len - 1) - defp pair_params_range(expr), do: 0..0 + defp where_params_offset(wheres) do + wheres + |> Enum.map(fn %Query.BooleanExpr{expr: expr} -> + _from..to = where_params_range(expr) + to + end) + |> Enum.max(& &1) + end + + defp where_params_range({_, _, [_, {:^, _, [0, 0]}]}), do: 0..0 + defp where_params_range({_, _, [_, {:^, _, [idx, len]}]}), do: idx..(idx + len - 1) + defp where_params_range({_, _, [_, {:^, _, [idx]}]}), do: idx..idx + defp where_params_range({:not, _, [expr]}), do: where_params_range(expr) + + defp where_params_range({_, _, exprs}) do + ranges = Enum.map(exprs, &where_params_range(&1)) |> Enum.filter(& &1) + from.._to = Enum.at(ranges, 0) + _from..to = Enum.at(ranges, -1) + from..to + end end diff --git a/test/mongo_ecto_test.exs b/test/mongo_ecto_test.exs index 9cf65f0..24beaa1 100644 --- a/test/mongo_ecto_test.exs +++ b/test/mongo_ecto_test.exs @@ -92,15 +92,24 @@ defmodule Mongo.EctoTest do end test "where in ids + dynamic limit + dynamic offset" do - post1 = TestRepo.insert!(%Post{}) - post2 = TestRepo.insert!(%Post{}) - post3 = TestRepo.insert!(%Post{}) + visits = 3 + exclude_visits = [4, 5] + post1 = TestRepo.insert!(%Post{visits: visits}) + post2 = TestRepo.insert!(%Post{visits: visits}) + post3 = TestRepo.insert!(%Post{visits: visits}) ids = [post1.id, post2.id, post3.id] limit = 1 - offset = 1 + offset = 2 - query = from p in Post, where: p.id in ^ids, limit: ^limit, offset: ^offset - assert TestRepo.all(query) == [post2] + query = + from( + p in Post, + where: p.visits == ^visits and not (p.visits in ^exclude_visits) and p.id in ^ids, + limit: ^limit, + offset: ^offset + ) + + assert TestRepo.all(query) == [post3] end # test "partial update in map" do