filter.ex (7021B)
1 # Zenflows is designed to implement the Valueflows vocabulary, 2 # written and maintained by srfsh <info@dyne.org>. 3 # Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU Affero General Public License as published by 7 # the Free Software Foundation, either version 3 of the License, or 8 # (at your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU Affero General Public License for more details. 14 # 15 # You should have received a copy of the GNU Affero General Public License 16 # along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 defmodule Zenflows.VF.Proposal.Filter do 19 @moduledoc false 20 21 import Ecto.Query 22 23 alias Ecto.{Changeset, Queryable} 24 alias Zenflows.DB.{ID, Page, Schema, Validate} 25 alias Zenflows.VF.Proposal 26 27 @spec all(Page.t()) :: {:ok, Queryable.t()} | {:error, Changeset.t()} 28 def all(%{filter: nil}), do: {:ok, Proposal} 29 def all(%{filter: params}) do 30 with {:ok, filters} <- all_validate(params) do 31 {:ok, Enum.reduce(filters, Proposal, &all_f(&2, &1))} 32 end 33 end 34 35 @spec all_f(Queryable.t(), {atom(), term()}) :: Queryable.t() 36 defp all_f(q, {:primary_intents_resource_inventoried_as_conforms_to, v}) do 37 q 38 |> join(:primary_intents_resource_inventoried_as) 39 |> where([primary_intents_resource_inventoried_as: r], r.conforms_to_id in ^v) 40 end 41 defp all_f(q, {:or_primary_intents_resource_inventoried_as_conforms_to, v}) do 42 q 43 |> join(:primary_intents_resource_inventoried_as) 44 |> or_where([primary_intents_resource_inventoried_as: r], r.conforms_to_id in ^v) 45 end 46 defp all_f(q, {:primary_intents_resource_inventoried_as_primary_accountable, v}) do 47 q 48 |> join(:primary_intents_resource_inventoried_as) 49 |> where([primary_intents_resource_inventoried_as: r], r.primary_accountable_id in ^v) 50 end 51 defp all_f(q, {:or_primary_intents_resource_inventoried_as_primary_accountable, v}) do 52 q 53 |> join(:primary_intents_resource_inventoried_as) 54 |> or_where([primary_intents_resource_inventoried_as: r], r.primary_accountable_id in ^v) 55 end 56 defp all_f(q, {:primary_intents_resource_inventoried_as_classified_as, v}) do 57 q 58 |> join(:primary_intents_resource_inventoried_as) 59 |> where([primary_intents_resource_inventoried_as: r], fragment("? @> ?", r.classified_as, ^v)) 60 end 61 defp all_f(q, {:or_primary_intents_resource_inventoried_as_classified_as, v}) do 62 q 63 |> join(:primary_intents_resource_inventoried_as) 64 |> or_where([primary_intents_resource_inventoried_as: r], fragment("? @> ?", r.classified_as, ^v)) 65 end 66 defp all_f(q, {:primary_intents_resource_inventoried_as_name, v}) do 67 q 68 |> join(:primary_intents_resource_inventoried_as) 69 |> where([primary_intents_resource_inventoried_as: r], ilike(r.name, ^"%#{v}%")) 70 end 71 defp all_f(q, {:or_primary_intents_resource_inventoried_as_name, v}) do 72 q 73 |> join(:primary_intents_resource_inventoried_as) 74 |> or_where([primary_intents_resource_inventoried_as: r], ilike(r.name, ^v)) 75 end 76 defp all_f(q, {:primary_intents_resource_inventoried_as_id, v}) do 77 q 78 |> join(:primary_intents_resource_inventoried_as) 79 |> where([primary_intents_resource_inventoried_as: r], r.id in ^v) 80 end 81 defp all_f(q, {:or_primary_intents_resource_inventoried_as_id, v}) do 82 q 83 |> join(:primary_intents_resource_inventoried_as) 84 |> or_where([primary_intents_resource_inventoried_as: r], r.id in ^v) 85 end 86 87 # join primary_intents 88 @spec join(Queryable.t(), atom()) :: Queryable.t() 89 defp join(q, :primary_intents) do 90 if has_named_binding?(q, :primary_intents), 91 do: q, 92 else: join(q, :inner, [x], pi in assoc(x, :primary_intents), as: :primary_intents) 93 end 94 # join resource_inventoried_as through primary_intents above 95 defp join(q, :primary_intents_resource_inventoried_as) do 96 q = join(q, :primary_intents) 97 if has_named_binding?(q, :primary_intents_resource_inventoried_as), 98 do: q, 99 else: join(q, :inner, [primary_intents: pi], r in assoc(pi, :resource_inventoried_as), 100 as: :primary_intents_resource_inventoried_as) 101 end 102 103 @spec all_validate(Schema.params()) 104 :: {:ok, Changeset.data()} | {:error, Changeset.t()} 105 defp all_validate(params) do 106 {%{}, %{ 107 primary_intents_resource_inventoried_as_conforms_to: {:array, ID}, 108 or_primary_intents_resource_inventoried_as_conforms_to: {:array, ID}, 109 primary_intents_resource_inventoried_as_primary_accountable: {:array, ID}, 110 or_primary_intents_resource_inventoried_as_primary_accountable: {:array, ID}, 111 primary_intents_resource_inventoried_as_classified_as: {:array, :string}, 112 or_primary_intents_resource_inventoried_as_classified_as: {:array, :string}, 113 primary_intents_resource_inventoried_as_name: :string, 114 or_primary_intents_resource_inventoried_as_name: :string, 115 primary_intents_resource_inventoried_as_id: {:array, ID}, 116 or_primary_intents_resource_inventoried_as_id: {:array, ID}, 117 }} 118 |> Changeset.cast(params, ~w[ 119 primary_intents_resource_inventoried_as_conforms_to 120 or_primary_intents_resource_inventoried_as_conforms_to 121 primary_intents_resource_inventoried_as_primary_accountable 122 or_primary_intents_resource_inventoried_as_primary_accountable 123 primary_intents_resource_inventoried_as_classified_as 124 or_primary_intents_resource_inventoried_as_classified_as 125 primary_intents_resource_inventoried_as_name 126 or_primary_intents_resource_inventoried_as_name 127 primary_intents_resource_inventoried_as_id 128 or_primary_intents_resource_inventoried_as_id 129 ]a) 130 |> Validate.class(:primary_intents_resource_inventoried_as_conforms_to) 131 |> Validate.class(:or_primary_intents_resource_inventoried_as_conforms_to) 132 |> Validate.class(:primary_intents_resource_inventoried_as_primary_accountable) 133 |> Validate.class(:or_primary_intents_resource_inventoried_as_primary_accountable) 134 |> Validate.class(:primary_intents_resource_inventoried_as_classified_as) 135 |> Validate.class(:or_primary_intents_resource_inventoried_as_classified_as) 136 |> Validate.name(:primary_intents_resource_inventoried_as_name) 137 |> Validate.name(:or_primary_intents_resource_inventoried_as_name) 138 |> Validate.exist_nand([:primary_intents_resource_inventoried_as_conforms_to, 139 :or_primary_intents_resource_inventoried_as_conforms_to]) 140 |> Validate.exist_nand([:primary_intents_resource_inventoried_as_primary_accountable, 141 :or_primary_intents_resource_inventoried_as_primary_accountable]) 142 |> Validate.exist_nand([:primary_intents_resource_inventoried_as_classified_as, 143 :or_primary_intents_resource_inventoried_as_classified_as]) 144 |> Validate.exist_nand([:primary_intents_resource_inventoried_as_name, 145 :or_primary_intents_resource_inventoried_as_name]) 146 |> Validate.exist_nand([:primary_intents_resource_inventoried_as_id, 147 :or_primary_intents_resource_inventoried_as_id]) 148 |> Validate.escape_like(:primary_intents_resource_inventoried_as_name) 149 |> Validate.escape_like(:or_primary_intents_resource_inventoried) 150 |> Changeset.apply_action(nil) 151 end 152 end