LMDBMulti
LMDBMulti[K, T] is the API facade for multi-value collections where a single key can hold multiple values.
Table of contents
- Overview
- Creating a multi-collection
- Read operations
- Write operations
- Transactions
- Cross-collection transactions with lift
- Example: event log
- Difference from LMDBCollection
Overview
A regular LMDBCollection enforces one-value-per-key semantics. LMDBMulti lifts that restriction: the same key can be associated with any number of values, and each (key, value) pair is stored as a distinct entry in the underlying B+ tree.
Typical use cases:
- Storing multiple tags or labels per entity
- Recording historical events per entity ID
- Adjacency lists for graph-like relationships
val tags: LMDBMulti[String, String] =
LMDB.multiCreate[String, String]("tags", failIfExists = false)
Creating a multi-collection
LMDB.multiCreate[K, T](name: String, failIfExists: Boolean = true): IO[..., LMDBMulti[K, T]]
| Parameter | Description |
|---|---|
name | Unique collection name within the database |
failIfExists | If true (default), fails when the collection already exists |
Read operations
fetch
def fetch(key: K): IO[FetchErrors, List[T]]
Returns all values stored under key. Returns an empty list if the key has no associated values.
tags.fetch("product-42").map { values =>
println(s"Tags: ${values.mkString(", ")}")
}
size
def size(): IO[SizeErrors, Long]
Returns the total number of (key, value) pairs across all keys in the collection (not the number of distinct keys).
Write operations
put
def put(key: K, document: T): IO[UpsertErrors, Unit]
Adds document as a new value under key. Existing values for the same key are preserved.
// Accumulate tags for a product
tags.put("product-42", "electronics") *>
tags.put("product-42", "sale") *>
tags.put("product-42", "new-arrival")
delete
def delete(key: K, document: T): IO[DeleteErrors, Boolean]
Removes a specific value from the set of values stored under key. Returns true if the value was found and removed.
tags.delete("product-42", "sale") // removes only the "sale" tag
deleteAll
def deleteAll(key: K): IO[DeleteErrors, Boolean]
Removes all values associated with key. Returns true if any values were present and removed.
tags.deleteAll("product-42") // removes the key and all its values
clear
def clear(): IO[ClearErrors, Unit]
Removes all entries from the collection.
Transactions
readOnly
def readOnly[R, E, A](f: LMDBMultiReadOps[K, T] => ZIO[R, E, A]): ZIO[R, E | StorageSystemError, A]
Executes read operations atomically within a single read-only transaction.
multiCol.readOnly { tx =>
for {
size <- tx.size()
items <- tx.fetch("my-key")
} yield (size, items)
}
The LMDBMultiReadOps facade provides exists(), size(), and fetch().
readWrite
def readWrite[R, E, A](f: LMDBMultiWriteOps[K, T] => ZIO[R, E, A]): ZIO[R, E | StorageSystemError | NestedWriteTransactionError, A]
Executes read and write operations atomically.
multiCol.readWrite { tx =>
tx.put("key-a", "value1") *>
tx.put("key-a", "value2") *>
tx.delete("key-b", "old-value")
}
The LMDBMultiWriteOps facade provides all read operations plus clear(), put(), delete(), and deleteAll().
Cross-collection transactions with lift
Use lift to participate in a global transaction that spans multiple collections or mix of regular and multi-value collections:
lmdb.readWrite { ops =>
val productsTx = products.lift(ops) // LMDBCollection
val tagsTx = tags.lift(ops) // LMDBMulti
productsTx.upsertOverwrite(productId, product) *>
tagsTx.put(productId, "featured")
}
See Transactions for a full guide.
Example: event log
import zio.*, zio.lmdb.*, zio.lmdb.json.*
import java.time.Instant
case class Event(action: String, timestamp: Instant) derives LMDBCodecJson
val program = for {
events <- LMDB.multiCreate[String, Event]("events", failIfExists = false)
// Record events for entity "user-1"
now <- Clock.instant
_ <- events.put("user-1", Event("login", now))
_ <- events.put("user-1", Event("action", now.plusSeconds(10)))
_ <- events.put("user-1", Event("logout", now.plusSeconds(300)))
// Retrieve all events for "user-1"
log <- events.fetch("user-1")
_ <- ZIO.foreachDiscard(log)(e => Console.printLine(e))
// Cleanup
_ <- events.deleteAll("user-1")
} yield ()
Difference from LMDBCollection
| Feature | LMDBCollection[K, T] | LMDBMulti[K, T] |
|---|---|---|
| Values per key | Exactly one | Zero or more |
| Insert semantics | insert (strict, fails on conflict) / upsertOverwrite (replaces) | put accumulates |
| Fetch result | Option[T] | List[T] |
| Streaming | stream(), streamWithKeys() | — |
| Cursor navigation | head, last, next, previous | — |
| Index support | Yes (via withIndex) | No |