View Source FDBC.Tuple (fdbc v0.1.4)

An implementation of upstreams builtin tuple layer. This layer is used for encoding keys that are useable by FoundationDB. The encoded key maintains the same sort order as the original tuple: sorted first by the first element, then by the second element, etc. This makes the tuple layer ideal for building a variety of higher-level data models.

Below is an example showing the usage of the tuple layer for handling population figures for the United States are stored using keys formed from the tuple of state and county:

defmodule Example do
  def get_county_populations_in_state(db, state) do
    FDBC.transact(db, fn tr ->
      {start, stop} = FDBC.Tuple.range([state])
      Transaction.get_range(tr, start, stop) |> Enum.map(fn {_, v} -> String.to_integer(v) end)
    end)
  end

  def set_county_population(db, state, county, population) do
    FDBC.transact(db, fn tr ->
      key = FDBC.Tuple.pack([state, county])
      Transaction.set(tr, key, Integer.to_string(population))
    end)
  end
end

Upstream Compatibility

At the time of writing all type codes are implemented.

Although fully complaint, the tuple layer implementation is lossy by default due to Elixir's type system. While not an issue when the key space is exclusively managed by this library, this is not the case if the key space is shared. This is because types like String.t/0 map directly down to binary/0 which is a lossy process at the time of packing.

# A string will encode to the type code of a binary and not of a unicode string.
assert "\x01foobar\x00" == Tuple.pack(["foobar"])

# This can be countered via a keyword list.
assert "\x02foobar\x00" == Tuple.pack([{string: "foobar"}])

To allow interoperability on shared key spaces the FDBC.Tuple module supports the :keyed option. When used this option changes the tuple from a list into a keyword list, where the keys are used to type the tuple.

# In this case `keyed` will ensure the tuple is a keyword list and raise an exception if not.
assert "\x02foobar\x00" == Tuple.pack([{string: "foobar"}], keyed: true)

# In this case `keyed` will ensure the tuple is returned as a keyword list.
assert [{string: "foobar"}] == Tuple.unpack("\x02foobar\x00", keyed: true)

The following atoms are used for keying tuple type codes:

  • :null - 0x00
  • :binary - 0x01
  • :string - 0x02
  • :integer - 0x0B..0x1D
  • :float - 0x20
  • :double - 0x21
  • :boolean - 0x26..0x27
  • :uuid - 0x30
  • :versionstamp - 0x33

User Type Codes

It is possible to extend the tuple layer with user type codes which can be achieved by implementing the FDBC.Tuple.Extension behaviour and supplying it to the tuple functions via the :extension option. For more information and example refer to the documentation in FDBC.Tuple.Extension.

Summary

Functions

Returns a key encoding the specified tuple.

Returns the start and stop keys for a given tuple.

Returns the tuple that encoded into the given key.

Functions

pack(tuple, opts \\ [])

@spec pack(
  [any()],
  keyword()
) :: binary()

Returns a key encoding the specified tuple.

Raises an ArgumentError if more than one incomplete FDBC.Tuple.Versionstamp is provided.

Options

  • :extension - a module that implements the behaviour FDBC.Tuple.Extension allowing the Tuple layer to be extended with user type codes.

  • :keyed - if present, then the tuple is returned as a keyword list, such as [{string: "flip"}, {string: "flop"}].

  • :versionstamp - if present, then the tuple must contain one incomplete versionstamp whose postition will be encoded on the end of the key to enable compatability with versionstamp operations; An ArgumentError will be raised if no incomplete versionstamp is found or if more than one is found. By default packing a tuple with an incomplete versionstamp will raise an ArgumentError.

range(prefix, opts \\ [])

@spec range(
  [any()],
  keyword()
) :: {binary(), binary()}

Returns the start and stop keys for a given tuple.

The start and stop keys allow the retrieval of all tuples of greater length than the provided prefix.

Options

  • :extension - a module that implements the behaviour FDBC.Tuple.Extension allowing the Tuple layer to be extended with user type codes.

  • :keyed - if present, then the tuple must be keyedly typed as a keyword list, such as [{string: "flip"}, {string: "flop"}].

Examples

# Returns start & stop keys which when used in a range query would return # tuples like ["A", 2, x], ["A", 2, x, y], etc. FDBC.Tuple.range(["A", 2])

unpack(key, opts \\ [])

@spec unpack(
  binary(),
  keyword()
) :: [any()]

Returns the tuple that encoded into the given key.

Options

  • :extension - a module that implements the behaviour FDBC.Tuple.Extension allowing the Tuple layer to be extended with user type codes.

  • :keyed - if present, then the tuple is returned as a keyword list, such as [{string: "flip"}, {string: "flop"}].