darnahsan
Custom filters on jsonb column using Flop
I am trying to implement custom filters on a table that has a jsonb column metadata using Flop. as per @woylie suggestions trying to use custom fields
Following the docs for custom fields came up with below code.
when trying to lookup metadata_is_active getting error
Postgrex expected a binary, got true. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.
<url>?filters[0][field]=name&filters[0][value]=Expert&filters[1][field]=whatsapp_no&filters[1][value]=88827271111&filters[2][field]=metadata_is_active&filters[2][value]=true
for metadata_owner it throws no error just returns empty result even on values that should match
<url>?filters[0][field]=name&filters[0][value]=Expert&filters[1][field]=whatsapp_no&filters[1][value]=88827271111&filters[2][field]=metadata_owner&filters[2][value]=abc
I feel my noobness with dynamic and fragmented queries is probably where I am wrong.
organization.ex
@derive {
Flop.Schema,
filterable: [:name, :whatsapp_no, :metadata_is_active, :metadata_owner],
sortable: [:name, :whatsapp_no, :inserted_at],
max_limit: 100,
default_limit: 50,
adapter_opts: [
custom_fields: [
metadata_is_active: [
filter: {OrganizationMetadataFilters, :metadata, []},
ecto_type: :boolean
],
metadata_owner: [
filter: {OrganizationMetadataFilters, :metadata, []},
ecto_type: :string
]
]
]
}
schema "organizations" do
field :name, :string
field :whatsapp_no, :string
embeds_one :metadata, OrganizationMetadata
organization_metadata_filters.ex
defmodule Maverick.Organizations.OrganizationMetadataFilters do
@moduledoc """
Maverick.Organizations.MetadataFilters
"""
import Ecto.Query
def metadata(query, %Flop.Filter{field: name, value: value, op: op} = flop_filter, _) do
metadata_field = field(name)
metadata_value = value(name, value)
expr =
dynamic(
[r],
fragment(
"metadata->>'?'",
field(r, ^metadata_field)
)
)
case metadata_value do
{:ok, query_value} ->
IO.inspect(flop_filter, label: "Flop filter")
IO.inspect(metadata_field, label: "Metadata field")
IO.inspect(query_value, label: "Metadata value")
conditions =
case op do
:== -> dynamic([r], ^expr == ^query_value)
:!= -> dynamic([r], ^expr != ^query_value)
:> -> dynamic([r], ^expr > ^query_value)
:< -> dynamic([r], ^expr < ^query_value)
:>= -> dynamic([r], ^expr >= ^query_value)
:<= -> dynamic([r], ^expr <= ^query_value)
end
IO.inspect(conditions, label: "conditions")
where(query, ^conditions)
:error ->
IO.inspect("Error casting value #{value} for #{name}")
query
end
end
def field(:metadata_is_active), do: :is_active
def field(:metadata_owner), do: :owner
def value(:metadata_is_active, value), do: Ecto.Type.cast(:boolean, value)
def value(:metadata_owner, value), do: Ecto.Type.cast(:string, value)
end
Marked As Solved
darnahsan
doing so fixes it for now
def dynamic_expr(:metadata_active) do
dynamic(
[r],
fragment(
"(?->>'active')::boolean",
field(r, :metadata)
)
)
end
def dynamic_expr(:metadata_owner) do
dynamic(
[r],
fragment(
"(?->>'owner')",
field(r, :metadata)
)
)
end
Flop RAW SQL
{"SELECT o0.\"id\", o0.\"name\", o0.\"whatsapp_no\", o0.\"metadata\", o0.\"inserted_at\", o0.\"updated_at\" FROM \"organizations\" AS o0 WHERE (o0.\"name\" = $1) AND (o0.\"whatsapp_no\" = $2) AND ((o0.\"metadata\"->>'active')::boolean = $3) ORDER BY o0.\"name\" LIMIT $4 OFFSET $5",
["Expert", "88827271111", true, 3, 0]}
Thanks for you help @woylie , I have another query for :in op, will discuss separately
Also Liked
woylie
With explicit type casting the 1st error is resolved but now I see the same empty result for both fields which mean they are not matching somehow.
Could you output the actual SQL? (Flop.validate → Flop.query → Ecto.Adapters.SQL.to_sql)
Also the wierd part is I see that
metadatafunction is called twice for each request, is that expected ?
If you use offset/page-based pagination, Flop makes a second query to retrieve the total count. The parameters are only validated once, but the where clauses are built once for the main query and once for the count query.
woylie
Try passing the for option:
query = Flop.query(Organization, parsed_filters, for: Organization)
Otherwise Flop wouldn’t know how about the schema configuration.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








