From 402d48b289c391669202136a2f88fa2fecdb3172 Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Tue, 30 May 2023 15:18:45 +0200 Subject: [PATCH] feat: Size operator (#35) * feat: Size operator * emric * fix: size operator * feat: remove raise * style: Refactor to reduce complexity * fix: Require logger * refactor: Update lib/exec.ex Co-authored-by: Emric Pichonnier * refactor: SImpler management --------- Co-authored-by: shiipou Co-authored-by: Emric Pichonnier Co-authored-by: Thomas DA ROCHA Co-authored-by: Emric Pichonnier --- lib/exec.ex | 108 +++++++++++++++++++++++++++--------------- lib/parser/grammar.ex | 2 +- test/exec_test.exs | 5 ++ test/parser_test.exs | 6 +-- 4 files changed, 78 insertions(+), 43 deletions(-) diff --git a/lib/exec.ex b/lib/exec.ex index c4ad663..b685f97 100644 --- a/lib/exec.ex +++ b/lib/exec.ex @@ -4,6 +4,8 @@ defmodule QueryParser.Exec do It takes care of executing the AST query into the data passed. """ + require Logger + alias LenraCommon.JsonHelper @all_operator ["$nin", "$not", "$nor"] @@ -86,18 +88,22 @@ defmodule QueryParser.Exec do ) when is_list(elem_value) do Enum.all?(operators, fn operator -> - case Map.get(operator, "operator") do - # $all need specific action - all_operator when all_operator == "$all" -> - exec?(operator, elem, ctx) - - # not operator need cond match all the list add value in @all_operator - op when op in @all_operator -> - exec_all(elem, elem_value, ctx, operator) - - # other operator need cond matchh any of the list - _common -> - exec_any(elem, elem_value, ctx, operator) + if Map.get(operator, "operator") == "$size" do + exec?(operator, elem, ctx) + else + case Map.get(operator, "operator") do + # $all need specific action + all_operator when all_operator == "$all" -> + exec?(operator, elem, ctx) + + # not operator need cond match all the list add value in @all_operator + op when op in @all_operator -> + exec_all(elem, elem_value, ctx, operator) + + # other operator need cond matchh any of the list + _common -> + exec_any(elem, elem_value, ctx, operator) + end end end) end @@ -128,33 +134,7 @@ defmodule QueryParser.Exec do ctx ) do {elem_value, ctx} = Map.pop(ctx, "elem_value") - - case operator do - "$eq" -> - elem_value == exec_value(value, elem, ctx) - - "$ne" -> - elem_value != exec_value(value, elem, ctx) - - "$lt" -> - elem_value < exec_value(value, elem, ctx) - - "$lte" -> - elem_value <= exec_value(value, elem, ctx) - - "$gt" -> - elem_value > exec_value(value, elem, ctx) - - "$gte" -> - elem_value >= exec_value(value, elem, ctx) - - "$exists" -> - nil? = nil == elem_value - exec_value(value, elem, ctx) != nil? - - "$type" -> - is_bson_type(elem_value, exec_value(value, elem, ctx)) - end + exec_value_operator?(operator, elem_value, exec_value(value, elem, ctx)) end defp exec?(%{"pos" => "leaf-clause", "key" => key, "value" => value}, elem, ctx) do @@ -204,4 +184,54 @@ defmodule QueryParser.Exec do end ) end + + defp exec_value_operator?("$eq", elem_value, value) do + elem_value == value + end + + defp exec_value_operator?("$ne", elem_value, value) do + elem_value != value + end + + defp exec_value_operator?("$lt", elem_value, value) do + elem_value < value + end + + defp exec_value_operator?("$lte", elem_value, value) do + elem_value <= value + end + + defp exec_value_operator?("$gt", elem_value, value) do + elem_value > value + end + + defp exec_value_operator?("$gte", elem_value, value) do + elem_value >= value + end + + defp exec_value_operator?("$exists", elem_value, value) when is_nil(elem_value) do + # Since elem_value is nil, we want to return the opposite of $exists value + !value + end + + defp exec_value_operator?("$exists", _elem_value, value) do + value + end + + defp exec_value_operator?("$size", elem_value, value) when is_list(elem_value) do + elem_value |> length() == value + end + + defp exec_value_operator?("$size", _elem_value, _value) do + false + end + + defp exec_value_operator?("$type", elem_value, value) do + is_bson_type(elem_value, value) + end + + defp exec_value_operator?(operator, _elem_value, _value) do + Logger.error("operator does not exist #{operator}") + false + end end diff --git a/lib/parser/grammar.ex b/lib/parser/grammar.ex index c512789..169709e 100644 --- a/lib/parser/grammar.ex +++ b/lib/parser/grammar.ex @@ -77,9 +77,9 @@ defmodule QueryParser.Parser.Grammar do '$lt' / '$eq' / '$ne' / + '$size' / '$type' / '$exists'" - # '$size' # '$bitsAllClear' / # '$bitsAllSet' / diff --git a/test/exec_test.exs b/test/exec_test.exs index 3fade46..9019d45 100644 --- a/test/exec_test.exs +++ b/test/exec_test.exs @@ -382,5 +382,10 @@ defmodule QueryParser.ExecTest do %{"_id" => 10} ] = parse_and_exec(data(), %{"_id" => %{"$not" => %{"$lt" => 5}}}) end + + test "size operator can only take array" do + assert [] = parse_and_exec(data(), %{"_id" => %{"$size" => 5}}) + assert [] = parse_and_exec(data(), %{"ids" => %{"$size" => 10}}) + end end end diff --git a/test/parser_test.exs b/test/parser_test.exs index d358629..ea38335 100644 --- a/test/parser_test.exs +++ b/test/parser_test.exs @@ -257,7 +257,7 @@ defmodule QueryParser.ParserTest do end test "should accept $size operator" do - not_supported(%{"foo" => %{"$size" => 10}}) + supported(%{"foo" => %{"$size" => 10}}) end test "should accept $regex operator without options (via leaf value)" do @@ -314,11 +314,11 @@ defmodule QueryParser.ParserTest do end test "should accept $not with an operator object as its value" do - not_supported(%{"names" => %{"$exists" => true, "$not" => %{"$size" => 0}}}) + supported(%{"names" => %{"$exists" => true, "$not" => %{"$size" => 0}}}) end test "should accept $not with a complex operator object as its value" do - not_supported(%{"names" => %{"$not" => %{"$exists" => true, "$size" => 0}}}) + supported(%{"names" => %{"$not" => %{"$exists" => true, "$size" => 0}}}) end test "should accept $not in combination with a $regex operator without options" do