diff --git a/README.md b/README.md index ae98fdd1..50ce47d3 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,231 @@ libraryDependencies += "com.outr" %% "lightdb-all" % "0.17.0" For a specific implementation like Lucene: ```scala libraryDependencies += "com.outr" %% "lightdb-lucene" % "0.17.0" -``` \ No newline at end of file +``` + +## Videos +Watch this [Java User Group demonstration of LightDB](https://www.youtube.com/live/E_5fwgbF4rc?si=cxyb0Br3oCEQInTW) + +## Getting Started + +This guide will walk you through setting up and using **LightDB**, a high-performance computational database. We'll use a sample application to explore its key features. + +--- + +## Prerequisites + +Ensure you have the following: + +- **Scala** installed +- **SBT** (Scala Build Tool) installed + +--- + +## Setup + +### Add LightDB to Your Project + +Add the following dependency to your `build.sbt` file: + +```scala +libraryDependencies += "com.outr" %% "lightdb-all" % "0.17.0" +``` + +--- + +## Example: Defining Models and Collections + +### Step 1: Define Your Models + +LightDB uses **Document** and **DocumentModel** for schema definitions. Here's an example of defining a `Person` and `City`: + +```scala +import lightdb._ +import lightdb.collection._ +import lightdb.doc._ +import fabric.rw._ + +case class Person( + name: String, + age: Int, + city: Option[City] = None, + nicknames: Set[String] = Set.empty, + friends: List[Id[Person]] = Nil, + _id: Id[Person] = Person.id() +) extends Document[Person] + +object Person extends DocumentModel[Person] with JsonConversion[Person] { + override implicit val rw: RW[Person] = RW.gen + + val name: I[String] = field.index("name", _.name) + val age: I[Int] = field.index("age", _.age) + val city: I[Option[City]] = field.index("city", _.city) + val nicknames: I[Set[String]] = field.index("nicknames", _.nicknames) + val friends: I[List[Id[Person]]] = field.index("friends", _.friends) +} +``` + +```scala +case class City(name: String) + +object City { + implicit val rw: RW[City] = RW.gen +} +``` + +### Step 2: Create the Database Class + +Define the database with collections for each model: + +```scala +import lightdb.sql._ +import lightdb.store._ +import lightdb.upgrade._ +import java.nio.file.Path + +class DB extends LightDB { + lazy val directory: Option[Path] = Some(Path.of(s"db/example")) + + lazy val people: Collection[Person, Person.type] = collection(Person) + + override def storeManager: StoreManager = SQLiteStore + + override def upgrades: List[DatabaseUpgrade] = Nil +} +``` + +--- + +## Using the Database + +### Step 1: Initialize the Database + +Instantiate and initialize the database: + +```scala +val db = new DB +// db: DB = repl.MdocSession$MdocApp$DB@6cd11793 +db.init() +// res0: Boolean = true +``` + +### Step 2: Insert Data + +Add records to the database: + +```scala +val adam = Person(name = "Adam", age = 21) +// adam: Person = Person( +// name = "Adam", +// age = 21, +// city = None, +// nicknames = Set(), +// friends = List(), +// _id = Id(value = "Mg3nibsB1wstqK1xEIiGNeU4Q5iw7Kfc") +// ) +db.people.transaction { implicit transaction => + db.people.insert(adam) +} +// res1: Person = Person( +// name = "Adam", +// age = 21, +// city = None, +// nicknames = Set(), +// friends = List(), +// _id = Id(value = "Mg3nibsB1wstqK1xEIiGNeU4Q5iw7Kfc") +// ) +``` + +### Step 3: Query Data + +Retrieve records using filters: + +```scala +db.people.transaction { implicit transaction => + val peopleIn20s = db.people.query.filter(_.age BETWEEN 20 -> 29).toList + println(peopleIn20s) +} +// List(Person(Adam,21,None,Set(),List(),Id(V4HuAlFgFWP0bARChPFtCx5eqMCvtX7l)), Person(Adam,21,None,Set(),List(),Id(xxVmia6PxlkFL1nWWDNFKiO2KccbDrhv)), Person(Adam,21,None,Set(),List(),Id(eaQIHd0ZiDHjxWa9VXzhybMHWtH80C47)), Person(Adam,21,None,Set(),List(),Id(Mg3nibsB1wstqK1xEIiGNeU4Q5iw7Kfc))) +``` + +--- + +## Features Highlight + +1. **Transactions:** + LightDB ensures atomic operations within transactions. + +2. **Indexes:** + Support for various indexes, like tokenized and field-based, ensures fast lookups. + +3. **Aggregation:** + Perform aggregations such as `min`, `max`, `avg`, and `sum`. + +4. **Streaming:** + Stream records for large-scale queries. + +5. **Backups and Restores:** + Backup and restore databases seamlessly. + +--- + +## Advanced Queries + +### Aggregations + +```scala +db.people.transaction { implicit transaction => + val results = db.people.query + .aggregate(p => List(p.age.min, p.age.max, p.age.avg, p.age.sum)) + .toList + println(results) +} +// List(MaterializedAggregate({"ageMin": 21, "ageMax": 21, "ageAvg": 21.0, "ageSum": 84},repl.MdocSession$MdocApp$Person$@5e5c66af)) +``` + +### Grouping + +```scala +db.people.transaction { implicit transaction => + val grouped = db.people.query.grouped(_.age).toList + println(grouped) +} +// List((21,List(Person(Adam,21,None,Set(),List(),Id(V4HuAlFgFWP0bARChPFtCx5eqMCvtX7l)), Person(Adam,21,None,Set(),List(),Id(xxVmia6PxlkFL1nWWDNFKiO2KccbDrhv)), Person(Adam,21,None,Set(),List(),Id(eaQIHd0ZiDHjxWa9VXzhybMHWtH80C47)), Person(Adam,21,None,Set(),List(),Id(Mg3nibsB1wstqK1xEIiGNeU4Q5iw7Kfc))))) +``` + +--- + +## Backup and Restore + +Backup your database: + +```scala +import lightdb.backup._ +import java.io.File + +DatabaseBackup.archive(db, new File("backup.zip")) +// res5: Int = 5 +``` + +Restore from a backup: + +```scala +DatabaseRestore.archive(db, new File("backup.zip")) +// res6: Int = 5 +``` + +--- + +## Clean Up + +Dispose of the database when done: + +```scala +db.dispose() +``` + +--- + +## Conclusion + +This guide provided an overview of using **LightDB**. Experiment with its features to explore the full potential of this high-performance database. For advanced use cases, consult the API documentation. \ No newline at end of file diff --git a/build.sbt b/build.sbt index 21120f98..855d932e 100644 --- a/build.sbt +++ b/build.sbt @@ -294,7 +294,7 @@ lazy val benchmark = project.in(file("benchmark")) lazy val docs = project .in(file("documentation")) - .dependsOn(core.jvm) + .dependsOn(all) .enablePlugins(MdocPlugin) .settings( mdocVariables := Map( diff --git a/core/src/main/scala/lightdb/backup/DatabaseBackup.scala b/core/src/main/scala/lightdb/backup/DatabaseBackup.scala index 98f9f0b2..45b36e84 100644 --- a/core/src/main/scala/lightdb/backup/DatabaseBackup.scala +++ b/core/src/main/scala/lightdb/backup/DatabaseBackup.scala @@ -18,7 +18,7 @@ object DatabaseBackup { */ def archive(db: LightDB, archive: File = new File("backup.zip")): Int = { - archive.getParentFile.mkdirs() + Option(archive.getParentFile).foreach(_.mkdirs()) if (archive.exists()) archive.delete() val out = new ZipOutputStream(new FileOutputStream(archive)) try { diff --git a/docs/README.md b/docs/README.md index 28af30d0..908ef175 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,4 +25,208 @@ libraryDependencies += "com.outr" %% "lightdb-all" % "@VERSION@" For a specific implementation like Lucene: ```scala libraryDependencies += "com.outr" %% "lightdb-lucene" % "@VERSION@" -``` \ No newline at end of file +``` + +## Videos +Watch this [Java User Group demonstration of LightDB](https://www.youtube.com/live/E_5fwgbF4rc?si=cxyb0Br3oCEQInTW) + +## Getting Started + +This guide will walk you through setting up and using **LightDB**, a high-performance computational database. We'll use a sample application to explore its key features. + +--- + +## Prerequisites + +Ensure you have the following: + +- **Scala** installed +- **SBT** (Scala Build Tool) installed + +--- + +## Setup + +### Add LightDB to Your Project + +Add the following dependency to your `build.sbt` file: + +```scala +libraryDependencies += "com.outr" %% "lightdb-all" % "@VERSION@" +``` + +--- + +## Example: Defining Models and Collections + +### Step 1: Define Your Models + +LightDB uses **Document** and **DocumentModel** for schema definitions. Here's an example of defining a `Person` and `City`: + +```scala mdoc +import lightdb._ +import lightdb.collection._ +import lightdb.doc._ +import fabric.rw._ + +case class Person( + name: String, + age: Int, + city: Option[City] = None, + nicknames: Set[String] = Set.empty, + friends: List[Id[Person]] = Nil, + _id: Id[Person] = Person.id() +) extends Document[Person] + +object Person extends DocumentModel[Person] with JsonConversion[Person] { + override implicit val rw: RW[Person] = RW.gen + + val name: I[String] = field.index("name", _.name) + val age: I[Int] = field.index("age", _.age) + val city: I[Option[City]] = field.index("city", _.city) + val nicknames: I[Set[String]] = field.index("nicknames", _.nicknames) + val friends: I[List[Id[Person]]] = field.index("friends", _.friends) +} +``` + +```scala mdoc +case class City(name: String) + +object City { + implicit val rw: RW[City] = RW.gen +} +``` + +### Step 2: Create the Database Class + +Define the database with collections for each model: + +```scala mdoc +import lightdb.sql._ +import lightdb.store._ +import lightdb.upgrade._ +import java.nio.file.Path + +class DB extends LightDB { + lazy val directory: Option[Path] = Some(Path.of(s"db/example")) + + lazy val people: Collection[Person, Person.type] = collection(Person) + + override def storeManager: StoreManager = SQLiteStore + + override def upgrades: List[DatabaseUpgrade] = Nil +} +``` + +--- + +## Using the Database + +### Step 1: Initialize the Database + +Instantiate and initialize the database: + +```scala mdoc +val db = new DB +db.init() +``` + +### Step 2: Insert Data + +Add records to the database: + +```scala mdoc +val adam = Person(name = "Adam", age = 21) +db.people.transaction { implicit transaction => + db.people.insert(adam) +} +``` + +### Step 3: Query Data + +Retrieve records using filters: + +```scala mdoc +db.people.transaction { implicit transaction => + val peopleIn20s = db.people.query.filter(_.age BETWEEN 20 -> 29).toList + println(peopleIn20s) +} +``` + +--- + +## Features Highlight + +1. **Transactions:** + LightDB ensures atomic operations within transactions. + +2. **Indexes:** + Support for various indexes, like tokenized and field-based, ensures fast lookups. + +3. **Aggregation:** + Perform aggregations such as `min`, `max`, `avg`, and `sum`. + +4. **Streaming:** + Stream records for large-scale queries. + +5. **Backups and Restores:** + Backup and restore databases seamlessly. + +--- + +## Advanced Queries + +### Aggregations + +```scala mdoc +db.people.transaction { implicit transaction => + val results = db.people.query + .aggregate(p => List(p.age.min, p.age.max, p.age.avg, p.age.sum)) + .toList + println(results) +} +``` + +### Grouping + +```scala mdoc +db.people.transaction { implicit transaction => + val grouped = db.people.query.grouped(_.age).toList + println(grouped) +} +``` + +--- + +## Backup and Restore + +Backup your database: + +```scala mdoc +import lightdb.backup._ +import java.io.File + +DatabaseBackup.archive(db, new File("backup.zip")) +``` + +Restore from a backup: + +```scala mdoc +DatabaseRestore.archive(db, new File("backup.zip")) +``` + +--- + +## Clean Up + +Dispose of the database when done: + +```scala mdoc +db.dispose() +``` + +--- + +## Conclusion + +This guide provided an overview of using **LightDB**. Experiment with its features to explore the full potential of this high-performance database. For advanced use cases, consult the API documentation. \ No newline at end of file