pub trait Collection: Send + Sync {
// Required methods
fn upsert(&self, doc_id: &str, data: Vec<u8>) -> Result<(), Error>;
fn get(&self, doc_id: &str) -> Result<Option<Vec<u8>>, Error>;
fn delete(&self, doc_id: &str) -> Result<(), Error>;
fn scan(&self) -> Result<Vec<(String, Vec<u8>)>, Error>;
fn find(
&self,
predicate: Box<dyn Fn(&[u8]) -> bool + Send>,
) -> Result<Vec<(String, Vec<u8>)>, Error>;
fn query_geohash_prefix(
&self,
prefix: &str,
) -> Result<Vec<(String, Vec<u8>)>, Error>;
fn count(&self) -> Result<usize, Error>;
}Expand description
Collection trait for storing and querying documents
A collection is a logical grouping of documents (key-value pairs). Documents are stored as raw bytes (typically serialized protobuf).
§Document Storage Format
- Key: String document ID (e.g., “cell-1”, “node-abc123”)
- Value: Raw bytes (serialized protobuf message)
§Thread Safety
All operations are thread-safe. Multiple threads can read/write concurrently.
§Example
let cells = storage.collection("cells");
// Create and store a cell
let cell = CellState { id: "cell-1".to_string(), ..Default::default() };
let bytes = cell.encode_to_vec();
cells.upsert("cell-1", bytes)?;
// Retrieve the cell
if let Some(stored) = cells.get("cell-1")? {
let cell = CellState::decode(&stored[..])?;
println!("Retrieved cell: {}", cell.id);
}
// Query all cells
for (id, bytes) in cells.scan()? {
let cell = CellState::decode(&bytes[..])?;
println!("Cell {}: {:?}", id, cell);
}Required Methods§
Sourcefn upsert(&self, doc_id: &str, data: Vec<u8>) -> Result<(), Error>
fn upsert(&self, doc_id: &str, data: Vec<u8>) -> Result<(), Error>
Insert or update a document
If a document with the given ID exists, it is replaced. Otherwise, a new document is created.
§Arguments
doc_id- Unique document identifierdata- Serialized document bytes (typically protobuf)
§Returns
Ok(()) on success, Err if upsert fails
§Example
let cell = CellState { id: "cell-1".to_string(), ..Default::default() };
cells.upsert("cell-1", cell.encode_to_vec())?;Sourcefn get(&self, doc_id: &str) -> Result<Option<Vec<u8>>, Error>
fn get(&self, doc_id: &str) -> Result<Option<Vec<u8>>, Error>
Get a document by ID
§Arguments
doc_id- Document identifier to retrieve
§Returns
Ok(Some(bytes))if document existsOk(None)if document not foundErrif query fails
§Example
match cells.get("cell-1")? {
Some(bytes) => {
let cell = CellState::decode(&bytes[..])?;
println!("Found cell: {}", cell.id);
}
None => println!("Cell not found"),
}Sourcefn scan(&self) -> Result<Vec<(String, Vec<u8>)>, Error>
fn scan(&self) -> Result<Vec<(String, Vec<u8>)>, Error>
Scan all documents in the collection
Returns all documents as (id, bytes) tuples. Order is implementation-defined.
§Performance
This loads all documents into memory. For large collections, consider streaming or pagination (future enhancement).
§Returns
Vector of (document_id, document_bytes) tuples
§Example
for (id, bytes) in cells.scan()? {
let cell = CellState::decode(&bytes[..])?;
println!("Cell {}: {:?}", id, cell);
}Sourcefn find(
&self,
predicate: Box<dyn Fn(&[u8]) -> bool + Send>,
) -> Result<Vec<(String, Vec<u8>)>, Error>
fn find( &self, predicate: Box<dyn Fn(&[u8]) -> bool + Send>, ) -> Result<Vec<(String, Vec<u8>)>, Error>
Find documents matching a predicate
Filters documents by applying a predicate function to their serialized bytes. Less efficient than indexed queries, but flexible.
§Arguments
predicate- Function that returns true for documents to include
§Returns
Vector of matching (document_id, document_bytes) tuples
§Example
// Find all cells with status "active"
let active_cells = cells.find(Box::new(|bytes| {
if let Ok(cell) = CellState::decode(bytes) {
cell.status == CellStatus::Active as i32
} else {
false
}
}))?;Sourcefn query_geohash_prefix(
&self,
prefix: &str,
) -> Result<Vec<(String, Vec<u8>)>, Error>
fn query_geohash_prefix( &self, prefix: &str, ) -> Result<Vec<(String, Vec<u8>)>, Error>
Query documents by geohash prefix (proximity queries)
Geohash is a hierarchical spatial index. Documents with the same prefix are geographically close. Longer prefixes = smaller areas.
§Arguments
prefix- Geohash prefix (e.g., “9q8y” for San Francisco area)
§Returns
Vector of matching (document_id, document_bytes) tuples
§Example
// Find all nodes within ~5km of a location
let nearby_nodes = nodes.query_geohash_prefix("9q8yy")?;§Implementation Notes
- For Automerge/redb: Uses prefix scan (requires geohash in key)
- For in-memory: Scans all documents (inefficient for large datasets)