Testfact versionHex DocsHex.pm

logo

A 🥾

Fact is an Elixir library that provides a file system based event store database.

Features

Coming soon...

Coming later...

Some time in the future...

Installation

The package can be installed by adding fact to your list of dependencies in mix.exs:

def deps do
  [
    {:fact, "~> 0.3.0"}
  ]
end

Then create a database instance.

$ mix fact.create -p data/turtles

Basic Usage

# Start a database instance
iex> {:ok, db} = Fact.open("data/turtles")

# Create an event
iex> event = %{
...>   type: "egg_hatched",
...>   data: %{
...>     name: "Turts"
...>   }
...> }

# Append the event to a stream
iex> {:ok, pos} = Fact.append_stream(db, event, "turtle-1")

# Read the event stream
iex> Fact.read(db, {:stream, "turtle-1"})
[
  %{
    "event_data" => %{"name" => "Turts"},
    "event_id" => "3bb4808303c847fd9ceb0a1251ef95da",
    "event_tags" => []
    "event_type" => "egg_hatched",
    "event_metadata" => %{},
    "store_position" => 1,
    "store_timestamp" => 1765039106962264,
    "stream_id" => "turtle-1",
    "stream_position" => 1
  }
]

Event Tags & Queries

# Start a database instance
iex> {:ok, db} = Fact.open("data/turtles")

# Create an event
iex> event = %{
...>   type: "clutch_laid",
...>   data: %{
...>     eggs_laid: 107
...>   },
...>   tags: ["clutch:c1"]
...> }

# Append the tagged event
iex> {:ok, pos} = Fact.append(db, event)

# Create and append another tagged event
iex> event = %{
...>   type: "egg_hatched",
...>   data: %{
...>     egg_id: 42
...>   },
...>   tags: ["clutch:c1", "egg:42"]
...> }
iex> {:ok, pos} = Fact.append(db, event)

# Query by the clutch tag that is defined on both appended events 
iex> import Fact.QueryItem
iex> Fact.read(db, {:query, tags("clutch:c1")})
[
  %{
    "event_data" => %{"eggs_laid" => 107},
    "event_id" => "0405bad46f1b407796c3e874c45664af",
    "event_metadata" => %{},
    "event_tags" => ["clutch:c1"],
    "event_type" => "clutch_laid",
    "store_position" => 2,
    "store_timestamp" => 1769814807951055
  },
  %{
    "event_data" => %{"egg_id" => 42},
    "event_id" => "7c7e42353fa045c4a6a4320ecb4591b7",
    "event_metadata" => %{},
    "event_tags" => ["clutch:c1", "egg:42"],
    "event_type" => "egg_hatched",
    "store_position" => 3,
    "store_timestamp" => 1769814855595808
  }
]

# Query by the egg tag that was only on one of the events
iex> Fact.read(db, {:query, tags("egg:42")})
[
  %{
    "event_data" => %{"egg_id" => 42, "name" => "Turts"},
    "event_id" => "7c7e42353fa045c4a6a4320ecb4591b7",
    "event_metadata" => %{},
    "event_tags" => ["clutch:c1", "egg:42"],
    "event_type" => "egg_hatched",
    "store_position" => 3,
    "store_timestamp" => 1769814855595808
  }
]

🦶🎶

1 - Its "pseudo-WORM" because immutability is enforced at the filesystem level by marking events as read-only. This prevents modification during normal operation, but does not provide hardware-level or regulatory WORM enforcement.