aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2025-02-03 13:09:29 +0200
committerJan Tuomi <jan@jantuomi.fi>2025-02-03 13:20:16 +0200
commit35687a08b48337c9989d59fa033580766c6e0947 (patch)
tree618c015f69f6af8cd620942719b989bcf1ac9b1c
parent258096c56bd9c3176a7210b29c18412bb93ed99a (diff)
Update README
-rw-r--r--README.md107
1 files changed, 83 insertions, 24 deletions
diff --git a/README.md b/README.md
index 41930e1..c068b36 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,10 @@ LogDB has the following features:
- In-memory indexes for fast lookups (primary and secondary)
- Log rotation and compaction for efficient storage even with larger databases
- Multiple concurrent readers and a single writer, using filesystem locks for synchronization
-- Simple data types: `Int`, `Float`, `String`, `Bytes` (arbitrary bytestring), and `Null`
+- Simple data types: `Int`, `Decimal`, `String`, `Bytes` (arbitrary bytestring), and `Null`
- A Rust API for interacting with the database, as well as Python bindings for the Rust API
+- Transactions based on eager exclusive locking
+- Batch read operations for improved performance
LogDB does not support:
@@ -17,10 +19,6 @@ LogDB does not support:
- Multiple tables
- Schema evolution, other than adding new nullable fields
-Possible future features:
-
-- Transactions
-
See the [ARCHITECTURE.md](ARCHITECTURE.md) document for more details on the design and implementation of LogDB.
## Inspiration
@@ -47,28 +45,89 @@ Then use it in your code like so:
```rust
use log_db::*;
-// Configure and initialize the database
-let mut db = DB::configure()
- .fields(vec![
- (Field::Id, ValueType::int()),
- (Field::Data, ValueType::bytes()),
- ])
- .primary_key(Field::Id)
- .initialize()?;
+// Define a type that represents your fields (columns)
+#[derive(Eq, PartialEq, Clone, Debug)]
+enum Field {
+ Id,
+ Name,
+}
+
+// Define your data type that represents a database row
+struct Inst {
+ pub id: i64,
+ pub name: Option<String>,
+}
+
+// Implement the `Recordable` trait for your data type
+impl Recordable for Inst {
+ // Use the `Field` enum
+ type Field = Field;
+
+ // Define the schema as a vector of field names and corresponding types
+ fn schema() -> Vec<(Self::Field, Type)> {
+ vec![
+ (Field::Id, Type::int()),
+ (Field::Name, Type::string().nullable()),
+ ]
+ }
+
+ // Select the primary key field
+ fn primary_key() -> Self::Field {
+ Field::Id
+ }
+
+ // Select the secondary key fields. All queries must be
+ // based on the primary key or secondary keys.
+ fn secondary_keys() -> Vec<Self::Field> {
+ vec![Field::Name]
+ }
+
+ // Describe how to convert the data type to a vector of `Value`s
+ fn into_record(self) -> Vec<Value> {
+ vec![
+ Value::Int(self.id),
+ match self.name {
+ Some(name) => Value::String(name),
+ None => Value::Null,
+ },
+ ]
+ }
+
+ // Similarly, describe how to convert a vector of database values to the data type
+ fn from_record(record: Vec<Value>) -> Self {
+ let mut it = record.into_iter();
+
+ Inst {
+ id: match it.next().unwrap() {
+ Value::Int(id) => id,
+ other => panic!("Invalid value type: {:?}", other),
+ },
+ name: match it.next().unwrap() {
+ Value::String(name) => Some(name),
+ Value::Null => None,
+ other => panic!("Invalid value type: {:?}", other),
+ },
+ }
+ }
+}
+
+fn main() {
+ // Initialize the database
+ let mut db = DB::<Inst>::configure()
+ .data_dir("data")
+ .initialize()?;
-// Define a record matching the `fields` schema
-let record = Record {
- values: vec![
- Value::Int(1),
- Value::Bytes(vec![1, 2, 3, 4]),
- ],
-};
+ // Insert or update the record based on the primary key
+ db.upsert(Inst {
+ id: 1,
+ name: Some("Alice".to_string()),
+ })?;
-// Insert or update the record based on the primary key (ID, first value)
-db.upsert(&record)?;
+ // Get the record by primary key
+ let found = db.get(Value::Int(1))?;
-// Get the record by primary key
-let found = db.get(Value::Int(1))?;
+ ...
+}
```
## Tests