Refine (Refine v0.2.0)
Copy MarkdownRefine is an Elixir library for implementing fast faceted search.
Summary
Facets table functions
Creates a facets table based on a source table and populates it with bitmap data, facet labels, and facet values.
Identical to create_facets_table/2 but in case the table already exists, skips table creation silently without returning an error.
Removes the facets table and all installed PostgreSQL triggers and functions.
Aggregates and applies membership changes from the deltas table to update the facets table.
Search functions
Performs a search on the source table using an optional base query and selected facet values.
Helper functions
Tests whether a table exists. Pass a qualified table name (including schema prefix) if the table is not in the "public" schema.
Facets table functions
@spec create_facets_table(facets_table_config(), [database_option()]) :: create_facets_table_return()
Creates a facets table based on a source table and populates it with bitmap data, facet labels, and facet values.
Alongside the creation of the table:
- Creates the
roaringbitmapextension (if it isn't created already). - With config option
add_identity_column_if_not_exists: adds an identity column to the source table (if no valid integer ID column is found in the table).
Configuration
See: Facets table configuration
Options
repo- The Ecto Repo module that contains a Postgres adapter. Omit when the repo is configured globally.timeout- Postgrex timeout option.
Examples
config = %{
facets_table: "articles_facets",
source_table: "articles",
add_identity_column_if_not_exists: true,
identity_column: "identity",
facets: [
%{facet_name: "draft"}
]
}
Refine.create_facets_table(config,
repo: MyApp.Repo
)
{:ok, "articles_facets"}To create a the facets table in a non-default schema:.
config = %{
facets_table: "classifications.categories_facets",
source_table: "classifications.categories",
...
}Returns an error if the table exists:
Refine.create_facets_table(config,
repo: MyApp.Repo
)
{:error, :facets_table_exists, "Table 'articles_facets' exists. ..."}To recreate the facets table:
Refine.drop_facets_table(config,
repo: MyApp.Repo
)
Refine.create_facets_table(config,
repo: MyApp.Repo
)
@spec create_facets_table_if_not_exists( facets_table_config(), [database_option()] ) :: create_facets_table_return()
Identical to create_facets_table/2 but in case the table already exists, skips table creation silently without returning an error.
If you need to recreate an existing facets table, first call drop_facets_table/2. See also: create_facets_table/2.
Options:
repo- The Ecto Repo module that contains a Postgres adapter. Omit when the repo is configured globally.timeout- Postgrex timeout option.
@spec drop_facets_table(facets_table_config(), [database_option()]) :: {:ok, String.t()} | {:error, :table_not_found} | {:error, Exception.t()}
Removes the facets table and all installed PostgreSQL triggers and functions.
Options:
repo- The Ecto Repo module that contains a Postgres adapter. Omit when the repo is configured globally.timeout- Postgrex timeout option.
Examples
Refine.drop_facets_table(config, repo: MyApp.Repo)
{:ok, "articles_facets"}
@spec merge_deltas(facets_table_config(), [database_option()]) :: :ok | {:error, [Exception.t()]}
Aggregates and applies membership changes from the deltas table to update the facets table.
Search functions
@spec search(facets_table_config(), [search_option()]) :: search_return()
Performs a search on the source table using an optional base query and selected facet values.
Parameter config is the configuration map used to create the facets table. Here it is used to read references to the source and facet tables.
Parameter options may include a base query, selected facet values, and fields to return in the results.
Examples
Return unfiltered rows in the source database:
Refine.search(config)Apply pagination with limit and offset:
Refine.search(config, limit: 10, offset: 10)Filter by facet values:
Refine.search(config,
facets: %{draft: ["true"]},
result_fields: [:id, :title]
)Filter a query by facet values:
query = from a in Article,
where: ilike(a.title, ^"%memory%"),
select: %{id: a.id, title: a.title}
facets = %{draft: ["true"]}
Refine.search(config,
query: query,
facets: facets
)Options
query
An Ecto query applied to the source table to filter or shape the data.
With where filter and select fields:
query = from a in Article,
where: ilike(a.title, ^"%memory%"),
select: %{id: a.id, title: a.title, draft: a.draft}For further options, see: Ecto.Query ➚.
facets
A map containing the selected values for each facet.
The map keys are facet names (as defined in the configuration), while the values are the selected option values stored in the facets table.
Facet values are always strings.
%{
draft: ["true"],
color: ["blue", "red"]
}If the list of facet options contains more than 1 value (blue and red), the filter on that facet is performed with an OR operator.
In the example above, the logic becomes: draft=true AND (color=blue OR color=red).
limit
Limits the number of rows returned. Default: 10.
offset
The number of skipped rows. Default: 0.
result_fields
List of source fields to include in the result list. Use this to limit returned data when not using query,
or to include columns not defined in the source Ecto schema. The values override any select expression in query.
result_fields = [:identity, :title]repo
The Ecto Repo module that contains a Postgres adapter. This can be omitted when the repo is configured globally - see: Installation.
timeout
Postgrex timeout option.
Helper functions
Types
@type create_facets_table_return() :: {:ok, String.t()} | {:error, :column_not_found, String.t()} | {:error, :column_names_not_unique} | {:error, :facets_table_exists, String.t()} | {:error, :identity_column_already_exists, String.t()} | {:error, :identity_column_invalid_type, String.t()} | {:error, :identity_column_not_found, String.t()} | {:error, :invalid_table_name, String.t()} | {:error, :table_names_not_unique} | {:error, Exception.t()}
@type database_option() :: {:repo, repo()} | postgrex_option()
@type facets_config() :: [facet_config()]
@type facets_table_config() :: %{ add_identity_column_if_not_exists: boolean() | nil, facets_table: String.t(), facets: facets_config(), identity_column: String.t(), joins: [join_config()] | nil, roaringbitmap_type: String.t() | nil, source_table: String.t() } | keyword()
@type repo() :: module()
@type search_option() :: {:facets, map()} | {:limit, integer()} | {:offset, integer()} | {:query, Ecto.Query.t()} | {:repo, repo()} | {:result_fields, [atom()]} | postgrex_option()
@type search_return() :: {:ok, search_return_data()} | {:error, Exception.t()}