Bag Tables
Bag tables store multiple values per key, but silently ignore duplicate key-value pairs. Use bags when you need to associate several distinct values with a single key — like tags, categories, or group memberships.
Create /app/data before opening the table. The examples assume a new
tags.dets file and follow one table from insertion through close.
Opening a Bag Table
Section titled “Opening a Bag Table”import gleam/dynamic/decodeimport shelf/bag
let assert Ok(table) = bag.open(name: "tags", path: "tags.dets", base_directory: "/app/data", key: decode.string, value: decode.string)For WriteThrough, open the table with open_config instead:
import gleam/dynamic/decodeimport shelfimport shelf/bag
let config = shelf.config(name: "tags", path: "tags.dets", base_directory: "/app/data") |> shelf.write_mode(shelf.WriteThrough)
let assert Ok(table) = bag.open_config(config: config, key: decode.string, value: decode.string)Writing Data
Section titled “Writing Data”// Insert a key-value pairlet assert Ok(Nil) = bag.insert(into: table, key: "color", value: "red")let assert Ok(Nil) = bag.insert(into: table, key: "color", value: "blue")
// Duplicate key-value pairs are silently ignoredlet assert Ok(Nil) = bag.insert(into: table, key: "color", value: "red")// "color" still has ["red", "blue"]
// Insert multiple entries at oncelet assert Ok(Nil) = bag.insert_list(into: table, entries: [ #("shape", "circle"), #("shape", "square"),])Reading Data
Section titled “Reading Data”Read the values you inserted from ETS:
// Contains "red" and "blue", with no repeated "red".let assert Ok(colors) = bag.lookup(from: table, key: "color")let assert Ok(True) = bag.member(of: table, key: "color")
// Counts objects, not unique keys.let assert Ok(count) = bag.size(of: table)let assert Ok(entries) = bag.to_list(from: table)Use fold to process each pair without first creating a list of the table:
let assert Ok(all_values) = bag.fold(over: table, from: [], with: fn(acc, _key, value) { [value, ..acc] })Deleting Data
Section titled “Deleting Data”// Delete all values for a keylet assert Ok(Nil) = bag.delete_key(from: table, key: "color")
// Delete a specific key-value pair (other values for the same key are kept)let assert Ok(Nil) = bag.delete_object(from: table, key: "shape", value: "circle")// "shape" still has ["square"]
// Delete all entrieslet assert Ok(Nil) = bag.delete_all(from: table)Save and close
Section titled “Save and close”These examples use WriteBack, so changes stay in ETS until you save or close the table. Successful close saves the remaining contents:
let assert Ok(Nil) = bag.close(table)Since the example deleted all entries, reopening this file gives you an empty table. See write modes to choose when to persist.
Bag vs Set
Section titled “Bag vs Set”| Behavior | Set | Bag |
|---|---|---|
| Values per key | One (overwrites) | Many (distinct only) |
lookup returns |
Single value | List(v) |
| Duplicate pairs | Overwrites | Silently ignored |
insert_new |
✅ | — |
update_counter |
✅ | — |