Fact.QueryItem (Fact v0.2.0)
View SourceProvides functions for constructing query item structures and converting them into query functions.
This module defines functions for building query items and combining them into lists.
In Fact, a query item is a struct which defines criteria for matching events, on event types,
event tags, and event data properties. A Fact database can be read using queries, and this
module provides to_function/1 for conveniently converting a single query item or list of
query items into a query function.
iex> Fact.QueryItem.tags("tag1")
%Fact.QueryItem{data: [], tags: ["tag1"], types: []}
iex> Fact.QueryItem.types("EventType1")
%Fact.QueryItem{data: [], tags: [], types: ["EventType1"]}
iex> Fact.QueryItem.data(name: "Jake")
%Fact.QueryItem{data: [name: ["Jake"]]}Query items can be combined using the pipe operator.
iex> Fact.QueryItem.tags("tag1") |> Fact.QueryItem.types("EventType1")
%Fact.QueryItem{data: [], tags: ["tag1"], types: ["EventType1"]}This module ensures query items are normalized and prevent duplicates.
iex> import Fact.QueryItem
iex> tags(["tag2","tag1"]) |> tags(["tag1","tag3","tag2"])
%Fact.QueryItem{data: [], tags: ["tag1", "tag2", "tag3"], types: []}
iex> types("EventType1") |> types(["EventType2","EventType1"])
%Fact.QueryItem{data: [], tags: [], types: ["EventType1", "EventType2"]}
iex> data(name: "Jake", name: "Cob", name: "Jacob") |> data(name: "Jake", name: "Statefarm")
%Fact.QueryItem{data: [name: ["Cob", "Jacob", "Jake", "Statefarm"]], tags: [], types: []}There are two special representations for query items, all/1 and none/1. These are typically used
as single query items when needed, but can be combined. Mathematically speaking, all/1 acts as the
identity, and none/0 acts as the
zero object in terms of combining query items.
iex> import Fact.QueryItem
iex> all()
:all
iex> none()
:noneMultiple query items can also be joined together to form a list which represents a compound query. At runtime, each query item is effectively combined with an OR, which often results in more Events being returned.
iex> import Fact.QueryItem
iex> join([
...> types(["EventType1","EventType2"]),
...> tags(["tag1", "tag2"]),
...> types(["EventType2","EventType3"]) |> tags(["tag1","tag3"])
...> ])Info
The normalization process used when joining may change the order of the query items. Ordering may not be consistent across different versions of OTP.
Summary
Functions
Returns a query item that matches all events.
Returns a query item that matches event data properties.
The hash/1 function produces a sha-1 hash of a query item or list of query items.
This combines multiple query items into a list of query items to describe a compound query.
Returns a query item that matches no events.
Returns a query item that matches events with all specified event tags.
Converts a query item or list of query items into a query function.
Returns a query item that matches events with any of the specified event types
Types
Functions
Returns a query item that matches all events.
When combined with another query item, it acts as the identity query item, and returns the specified query item.
iex> import Fact.QueryItem
iex> all()
:all
iex> tags("tag1") |> all()
%Fact.QueryItem{data: [], tags: ["tag1"], types: []}
iex> all() |> tags("tag1")
%Fact.QueryItem{data: [], tags: ["tag1"], types: []}
Returns a query item that matches event data properties.
When duplicate keys are specified the individual values are evaluated as an OR when the query is executed.
iex> Fact.QueryItem.data(name: "Jake", name: "Jacob")
%Fact.QueryItem{data: [name: ["Jacob", "Jake"]], tags: [], types: []}In SQL terms, assuming event_data is a jsonb column, this would be equalivant to:
SELECT *
FROM events
WHERE event_data->>'name' IN ('Jacob', 'Jake')Distinct keys are effectively an AND when the query is executed.
iex> Fact.QueryItem.data(name: "Jake", name: "Jacob", hobby: "Homebrewing")
%Fact.QueryItem{
data: [hobby: ["Homebrewing"], name: ["Jacob", "Jake"]],
tags: [],
types: []
}In SQL terms, assuming event_data is a jsonb column, this would be equalivant to:
SELECT *
FROM events
WHERE event_data->>'hobby' = 'Homebrewing'
AND event_data->>'name' IN ('Jacob', 'Jake')Duplicate key value pairs are ignored.
iex> import Fact.QueryItem
iex> data(name: "Jake", name: "Jacob", name: "Jacob") |> data(name: "Jake")
%Fact.QueryItem{data: [name: ["Jacob", "Jake"]], tags: [], types: []}Raises an ArgumentError when an invalid tag (not a string) is specified:
iex> Fact.QueryItem.data({"key", "value"})
** (ArgumentError) invalid data keyword
iex> Fact.QueryItem.data([{"key", "value"}])
** (ArgumentError) invalid data keyword
The hash/1 function produces a sha-1 hash of a query item or list of query items.
This function is used internally for normalization and caching.
This combines multiple query items into a list of query items to describe a compound query.
Each query item is effectively combined with an OR.
iex> import Fact.QueryItem
iex> join([
...> types(["EventType1","EventType2"]),
...> tags(["tag1", "tag2"]),
...> types(["EventType2","EventType3"]) |> tags(["tag1","tag3"])
...> ])In SQL terms, this would be equivalent to:
SELECT e.*
FROM events e
WHERE
(EXISTS (
SELECT 1 FROM event_tags t
WHERE e.event_id = t.event_id AND t.tag = 'tag1')
AND EXISTS (
SELECT 1 FROM event_tags t
WHERE e.event_id = t.event_id AND t.tag = 'tag2'))
OR e.event_type IN ('EventType1', 'EventType2')
OR (EXISTS (
SELECT 1 FROM event_tags t
WHERE e.event_id = t.event_id AND t.tag = 'tag1')
AND EXISTS (
SELECT 1 FROM event_tags t
WHERE e.event_id = t.event_id AND t.tag = 'tag3')
AND e.event_type IN ('EventType2', 'EventType3'))Duplicate query items are ignored.
iex> import Fact.QueryItem
iex> join([
...> tags(["tag1","tag2"]),
...> tags(["tag2","tag1"])
...> ])
%Fact.QueryItem{data: [], tags: ["tag1", "tag2"], types: []}Joining all/1 with any other query items will produce :all. In SQL, this is equivalent to OR true.
iex> import Fact.QueryItem
iex> join([
...> tags(["tag1","tag2"]),
...> all()
...> ])
:allJoining none/1 with any other query items will be ignored. In SQL, this is equivalent to OR false.
iex> import Fact.QueryItem
iex> join([
...> tags(["tag1","tag2"]),
...> none()
...> ])
%Fact.QueryItem{data: [], tags: ["tag1", "tag2"], types: []}Raises an ArgumentError when an invalid query item is supplied.
iex> Fact.QueryItem.join([:invalid_query_item])
** (ArgumentError) invalid query item
Returns a query item that matches no events.
When combined with another query item, it acts as the zero object, and returns :none.
iex> import Fact.QueryItem
iex> none()
:none
iex> tags("tag1") |> none()
:none
iex> none() |> tags("tag1")
:none
@spec tags(t(), Fact.event_tag() | [Fact.event_tag(), ...]) :: t()
Returns a query item that matches events with all specified event tags.
Multiple tags are effectively an AND when a query is evaluated.
iex> Fact.QueryItem.tags(["tag1", "tag2"])
%Fact.QueryItem{data: [], tags: ["tag1", "tag2"], types: []}In SQL terms, this would be equivalent to:
SELECT e.*
FROM events e
WHERE EXISTS (
SELECT 1 FROM event_tags t
WHERE e.event_id = t.event_id AND t.tag = 'tag1')
AND EXISTS (
SELECT 1 FROM event_tags t
WHERE e.event_id = t.event_id AND t.tag = 'tag2')Duplicate tags are ignored.
iex> import Fact.QueryItem
iex> tags("tag1") |> tags(["tag1", "tag2", "tag2"])
%Fact.QueryItem{data: [], tags: ["tag1", "tag2"], types: []}Raises an ArgumentError when an invalid tag (not a string) is specified:
iex> Fact.QueryItem.tags(:not_a_tag)
** (ArgumentError) invalid event tag
iex> Fact.QueryItem.tags([:not_a_tag])
** (ArgumentError) invalid event tag
@spec to_function(t() | [t(), ...]) :: Fact.Query.t()
Converts a query item or list of query items into a query function.
iex> import Fact.QueryItem
iex> fun1 = tags("tag1") |> to_function()
iex> is_function(fun1, 1)
:true
iex> fun2 = join([
...> types(["EventType1","EventType2"]),
...> tags(["tag1", "tag2"]),
...> types(["EventType2","EventType3"]) |> tags(["tag1","tag3"])
...> ]) |> to_function()
iex> is_function(fun2, 1)
:true
@spec types(t(), Fact.event_type() | [Fact.event_type(), ...]) :: t()
Returns a query item that matches events with any of the specified event types
Multiple event types are effectively an OR when a query is evaluated.
iex> Fact.QueryItem.types(["EventType1", "EventType2"])
%Fact.QueryItem{data: [], tags: [], types: ["EventType1", "EventType2"]}In SQL terms, this would be equivalent to:
SELECT *
FROM events
WHERE event_type IN ('EventType1', 'EventType2')Duplicate types are ignored.
iex> import Fact.QueryItem
iex> types(["EventType1", "EventType2"]) |> types(["EventType1", "EventType2"])
%Fact.QueryItem{data: [], tags: [], types: ["EventType1", "EventType2"]}Raises an ArgumentError when an invalid type (not a string) is specified:
iex> Fact.QueryItem.types(:not_a_type)
** (ArgumentError) invalid event type
iex> Fact.QueryItem.types([:not_a_type])
** (ArgumentError) invalid event type