Rummage.Ecto v2.0.0 Rummage.Ecto.CustomHook.SimpleSearch View Source

Rummage.Ecto.CustomHook.SimpleSearch is an example of a Custom Hook that comes with Rummage.Ecto.

This module provides a operations that can add searching functionality to a pipeline of Ecto queries. This module works by taking fields, and search_type and search_term.

This module doesn't support associations and hence is a simple alternative to Rummage's default search hook.

NOTE: This module doesn't return a list of entries, but a Ecto.Query.t. This module uses Rummage.Ecto.Hook.


ABOUT:

Arguments:

This Hook expects a queryable (an Ecto.Queryable) and search_params (a Map). The map should be in the format: %{field_name: %{search_term: true, search_type: :eq}}

Details:

  • field_name: The field name to search by.
  • search_term: Term to compare the field_name against.
  • search_type: Determines the kind of search to perform. If :eq, it
              expects the `field_name`'s value to be equal to `search_term`,
              If `lt`, it expects it to be less than `search_term`.
              To see all the `search_type`s, check
              `Rummage.Ecto.Services.BuildSearchQuery`
  • search_expr: This is optional. Defaults to :where. This is the way the
               search expression is appended to the existing query.
               To see all the `search_expr`s, check
               `Rummage.Ecto.Services.BuildSearchQuery`

For example, if we want to search products with available = true, we would do the following:

Rummage.Ecto.CustomHook.SimpleSearch.run(Product, %{available:
  %{search_type: :eq,
  search_term: true}})

This can be used for a search with multiple fields as well. Say, we want to search for products that are available, but have a price less than 10.0.

Rummage.Ecto.CustomHook.SimpleSearch.run(Product,
  %{available: %{search_type: :eq,
    search_term: true},
  %{price: %{search_type: :lt,
    search_term: 10.0}})

Assoications:

This module doesn't support assocations.


ASSUMPTIONS/NOTES:

  • This Hook assumes that the searched field is a part of the schema passed as the queryable.
  • This Hook has the default search_type of :eq.
  • This Hook has the default search_expr of :where.

USAGE:

For a regular search:

This returns a queryable which upon running will give a list of Parent(s) searched by ascending field_1

alias Rummage.Ecto.CustomHook.SimpleSearch

searched_queryable = SimpleSearch.run(Parent,
  %{field_1: %{search_type: :like, search_term: "field_!"}}})

For a case-insensitive search:

This returns a queryable which upon running will give a list of Parent(s) searched by ascending case insensitive field_1.

Keep in mind that case_insensitive can only be called for text fields

alias Rummage.Ecto.CustomHook.SimpleSearch

searched_queryable = SimpleSearch.run(Parent,
  %{field_1: %{ search_type: "ilike", search_term: "field_!"}}})

There are many other search_types. Check out Rummage.Ecto.Services.BuildSearchQuery docs to explore more search_types.

This module can be used by overriding the default module. This can be done in the following ways:

In the Rummage.Ecto call:

Rummage.Ecto.rummage(queryable, rummage,
  search: Rummage.Ecto.CustomHook.SimpleSearch)

or

MySchema.rummage(rummage, search: Rummage.Ecto.CustomHook.SimpleSearch)

OR

Globally for all models in config.exs:

config :my_app,
  Rummage.Ecto,
  search: Rummage.Ecto.CustomHook.SimpleSearch

OR

When using Rummage.Ecto with an Ecto.Schema:

defmodule MySchema do
  use Rummage.Ecto, repo: SomeRepo,
    search: Rummage.Ecto.CustomHook.SimpleSearch
end

Link to this section Summary

Functions

Callback implementation for Rummage.Ecto.Hook.format_params/3.

This is the callback implementation of Rummage.Ecto.Hook.run/2.

Link to this section Functions

Link to this function

format_params(queryable, params, opts)

View Source

Specs

format_params(Ecto.Query.t(), map(), keyword()) :: map()
format_params(Ecto.Query.t(), map(), keyword()) :: map()

Callback implementation for Rummage.Ecto.Hook.format_params/3.

This function ensures that params for each field have keys assoc, search_type and search_expr which are essential for running this hook module.

Examples

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> SimpleSearch.format_params(Parent, %{field: %{}}, [])
%{field: %{search_expr: :where, search_type: :eq}}

Specs

run(Ecto.Query.t(), map()) :: Ecto.Query.t()
run(Ecto.Query.t(), map()) :: Ecto.Query.t()

This is the callback implementation of Rummage.Ecto.Hook.run/2.

Builds a search Ecto.Query.t on top of a given Ecto.Query.t variable with given params.

Besides an Ecto.Query.t an Ecto.Schema module can also be passed as it implements Ecto.Queryable

Params is a Map, keys of which are field names which will be searched for and value corresponding to that key is a list of params for that key, which should include the keys: #{Enum.join(@expected_keys, ", ")}.

This function expects a search_expr, search_type. The search_term is what the field will be matched to based on the search_type and search_expr.

If no search_expr is given, it defaults to where.

For all search_exprs, refer to Rummage.Ecto.Services.BuildSearchQuery.

For all search_types, refer to Rummage.Ecto.Services.BuildSearchQuery.

If an expected key isn't given, a Runtime Error is raised.

NOTE:This hook isn't responsible for doing type validations. That's the responsibility of the user sending search_term and search_type.

Examples

When search_params are empty, it simply returns the same queryable:

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> SimpleSearch.run(Parent, %{})
Parent

When a non-empty map is passed as a field params, but with a missing key:

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> SimpleSearch.run(Parent, %{field: %{search_type: :eq}})
** (RuntimeError) Error in params, No values given for keys: search_term

When a valid map of params is passed with an Ecto.Schema module:

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> search_params = %{field1: %{
...> search_type: :like,
...> search_term: "field1",
...> search_expr: :where}}
iex> SimpleSearch.run(Rummage.Ecto.Product, search_params)
#Ecto.Query<from p0 in subquery(from p0 in Rummage.Ecto.Product), where: like(p0.field1, ^"%field1%")>

When a valid map of params is passed with an Ecto.Query.t:

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> search_params = %{field1: %{
...> search_type: :like,
...> search_term: "field1",
...> search_expr: :where}}
iex> query = from p0 in "products"
iex> SimpleSearch.run(query, search_params)
#Ecto.Query<from p0 in subquery(from p0 in "products"), where: like(p0.field1, ^"%field1%")>

When a valid map of params is passed with an Ecto.Query.t and :on_where:

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> search_params = %{field1: %{
...> search_type: :like,
...> search_term: "field1",
...> search_expr: :or_where}}
iex> query = from p0 in "products"
iex> SimpleSearch.run(query, search_params)
#Ecto.Query<from p0 in subquery(from p0 in "products"), or_where: like(p0.field1, ^"%field1%")>

When a valid map of params is passed with an Ecto.Query.t, searching on a boolean param

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> search_params = %{available: %{
...> search_type: :eq,
...> search_term: true,
...> search_expr: :where}}
iex> query = from p0 in "products"
iex> SimpleSearch.run(query, search_params)
#Ecto.Query<from p0 in subquery(from p0 in "products"), where: p0.available == ^true>

When a valid map of params is passed with an Ecto.Query.t, searching on a float param

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> search_params = %{price: %{
...> search_type: :gteq,
...> search_term: 10.0,
...> search_expr: :where}}
iex> query = from p0 in "products"
iex> SimpleSearch.run(query, search_params)
#Ecto.Query<from p0 in subquery(from p0 in "products"), where: p0.price >= ^10.0>

When a valid map of params is passed with an Ecto.Query.t, searching on a boolean param, but with a wrong search_type. NOTE: This doesn't validate the search_type of search_term

iex> alias Rummage.Ecto.CustomHook.SimpleSearch
iex> import Ecto.Query
iex> search_params = %{available: %{
...> search_type: :ilike,
...> search_term: true,
...> search_expr: :where}}
iex> query = from p0 in "products"
iex> SimpleSearch.run(query, search_params)
** (ArgumentError) argument error