diff --git a/flutter-sdk/collection.md b/flutter-sdk/collection/index.yml similarity index 69% rename from flutter-sdk/collection.md rename to flutter-sdk/collection/index.yml index ac6415b..8705e76 100644 --- a/flutter-sdk/collection.md +++ b/flutter-sdk/collection/index.yml @@ -1,5 +1,3 @@ ---- -label: Nitrite Collection icon: stack -order: 17 ---- \ No newline at end of file +label: Nitrite Collection +order: 17 \ No newline at end of file diff --git a/flutter-sdk/collection/intro.md b/flutter-sdk/collection/intro.md new file mode 100644 index 0000000..f3a8c0b --- /dev/null +++ b/flutter-sdk/collection/intro.md @@ -0,0 +1,40 @@ +--- +label: Introduction +icon: info +order: 17 +--- + +`NitriteCollection` represents a named document collection stored in a Nitrite database. It persists documents in a Nitrite database. It is similar to a table in relational database or a collection in MongoDB. + +Each document in a collection is associated with a unique `NitriteId`. It exposes a set of methods to perform CRUD operations on documents. It also supports indexing and querying. It also supports event based notification on document changes. + +## Creating a Collection + +A `NitriteCollection` can be created using `Nitrite` class. You need to call `getCollection()` method on `Nitrite` class to get an instance of a `Future`. + +If the collection does not exist, then it will be created automatically. If a collection with the same name already exists, then it will return the existing collection. + +```dart +var db = await nitriteBuilder() + .openOrCreate(); + +var collection = await db.getCollection("myCollection"); +``` + +## Limitations on Collection Name + +A collection name cannot be `null` or empty string. It cannot contains any of the following characters: + +- `|` (pipe) +- `:` (colon) +- `+` (plus) + +The name also cannot be any of the following reserved words: + +- $nitrite_users +- $nitrite_index_meta +- $nitrite_index +- $nitrite_meta_map +- $nitrite_store_info +- $nitrite_catalog + diff --git a/flutter-sdk/collection/read.md b/flutter-sdk/collection/read.md new file mode 100644 index 0000000..2f16ce0 --- /dev/null +++ b/flutter-sdk/collection/read.md @@ -0,0 +1,298 @@ +--- +label: Read Operations +icon: search +order: 15 +--- + +## Find Operations + +You can search documents in a collection using `find()` method. There are several overloaded version of `find()` method using a filter or find options or both. You can also search a document using it's `NitriteId`. + +### Filters + +Nitrite uses filters to find documents in a collection. A filter is a simple expression which evaluates to `true` or `false`. More information about filters can be found [here](../filter.md). + +### Find All Documents + +You can find all documents in a collection by calling `find()`. It returns a `DocumentCursor` object. + +```dart +var cursor = collection.find(); +``` + +### Finding a Document Using NitriteId + +You can find a document using it's `NitriteId` by calling `getById()`. It takes a `NitriteId` as input parameter. It returns a `Future` object if the document is found. Otherwise, it returns `null`. + +```dart +var doc = await collection.getById(someNitriteId); +``` + +### Finding a Document Using a Filter + +You can find a document using a filter. It takes a `Filter` object as input parameter. It returns a `DocumentCursor` object. + +If there is an index on the field specified in the filter, this operation will use the index to find the document. Otherwise, it will scan the entire collection to find the document. + +```dart +var cursor = collection.find(where("firstName").eq("John")); +``` + +### Finding a Document Using a Filter and Options + +You can find a document using a filter and options. It takes a `Filter` object as the first input parameter. It takes a `FindOptions` object as the second input parameter. It returns a `DocumentCursor` object. + +#### FindOptions + +`FindOptions` is a class that contains several options for find operation. It has the following options: + +- `limit`: It specifies the maximum number of documents to be returned by the find operation. +- `skip`: It specifies the number of documents to be skipped from the beginning of the result set. +- `orderBy`: It specifies a collection of fields to be sorted by, along with sort order for each field. The sort order can be `SortOrder.ascending` or `SortOrder.descending`. +- `distinct`: It specifies if the find operation should return distinct documents. If this option is `true`, then it will return only the distinct documents. Otherwise, it will return all the documents matched by the filter. + +```dart +var findOptions = FindOptions(); +findOptions.setSkip(10) + ..setLimit(10) + ..thenOrderBy("firstName", SortOrder.descending) + ..withDistinct(true); + +var cursor = collection.find(filter: where("firstName").eq("John"), findOptions: findOptions); +``` + +## DocumentCursor + +`DocumentCursor` represents a result set of a find operation. It provides methods to iterate over the result of a find operation and retrieve the documents. It also provides methods like projection, join etc. to get the desired result. + +### Iterating Over Documents + +The `DocumentCursor` extends `Stream`. So, you can iterate over the documents using await for loop. + +```dart +var cursor = collection.find(where("firstName").eq("John")); + +await for (var doc in cursor) { + // do something with the document +} +``` + +!!!info +A `DocumentCursor` is a stream of document. It does not load all the documents in memory at once. It loads the documents in memory as needed. So, it is memory efficient. +!!! + +### Getting the Documents + +You can get the documents from a `DocumentCursor` using `toList()` method. It returns a `Future>` object. + +```dart +var cursor = collection.find(where("firstName").eq("John")); + +var docs = await cursor.toList(); +``` + +### Getting the First Document + +You can get the first document from a `DocumentCursor` using `first` getter. It returns a `Future` object. + +```dart +var cursor = collection.find(where("firstName").eq("John")); + +var doc = await cursor.first; +``` + +### Getting the Size of the Result Set + +You can get the size of the result set from a `DocumentCursor` using `length` getter. It returns a `Future` object. + +```dart +var cursor = collection.find(where("firstName").eq("John")); + +var size = await cursor.length; +``` + +### Projection + +You can project a field or a set of fields from a `DocumentCursor` using `project()` method. It takes a `Document` as input parameter. It returns a `Stream` object. + +The document must contain only the fields that needs to be projected. The field values must be `null` or a nested document. The condition holds true for nested documents as well. + +Let's say you have a document like this: + +```json +{ + "firstName": "John", + "lastName": "Doe", + "address": { + "street": "123 Main Street", + "city": "New York" + } +} +``` + +And you want to project only `lastName` and `address.street` fields. Then you can do it like this: + +```dart +var cursor = collection.find(where("firstName").eq("John")); + +var projection = createDocument("lastName", null) + ..put("address", createDocument("street", null)); + +var projectedCursor = cursor.project(projection); +``` + +The result set will contain only `lastName` and `address.street` fields. + +```json +[ + { + "lastName": "Doe", + "address": { + "street": "123 Main Street" + } + } +] +``` + +### Join + +You can join two `DocumentCursor` using `leftJoin()` method. It takes another `DocumentCursor` as input parameter along with a `Lookup` object. It returns a `Stream` object. + +The join operation is similar to SQL left outer join operation. It takes two cursors and a lookup object. The lookup object contains the join condition. + +Let's say you have two collections `users` and `orders`. The `users` collection contains the following documents: + +```json +{ + "userId": 1, + "firstName": "John", + "lastName": "Doe" +} +``` + +```json +{ + "userId": 2, + "firstName": "Jane", + "lastName": "Doe" +} +``` + +And the `orders` collection contains the following documents: + +```json +{ + "orderId": 1, + "userId": 1, + "orderDate": "2018-01-01" +} +``` + +```json +{ + "orderId": 2, + "userId": 1, + "orderDate": "2018-01-02" +} +``` + +```json +{ + "orderId": 3, + "userId": 2, + "orderDate": "2018-01-03" +} +``` + +```json +{ + "orderId": 4, + "userId": 2, + "orderDate": "2018-01-04" +} +``` + +And you want to join these two collections on `userId` field. Then you can do it like this: + +```dart +var userCollection = await db.collection("users"); +var orderCollection = await db.collection("orders"); + +var users = userCollection.find(); +var orders = orderCollection.find(); + +var lookup = Lookup(); +lookup.setLocalField("userId"); +lookup.setForeignField("userId"); +lookup.setTargetField("orders"); + +var joinedCursor = users.leftJoin(orders, lookup); +``` + +The result set will contain the following documents: + +```json +{ + "userId": 1, + "firstName": "John", + "lastName": "Doe", + "orders": [ + { + "orderId": 1, + "userId": 1, + "orderDate": "2018-01-01" + }, + { + "orderId": 2, + "userId": 1, + "orderDate": "2018-01-02" + } + ] +} +``` + +```json +{ + "userId": 2, + "firstName": "Jane", + "lastName": "Doe", + "orders": [ + { + "orderId": 3, + "userId": 2, + "orderDate": "2018-01-03" + }, + { + "orderId": 4, + "userId": 2, + "orderDate": "2018-01-04" + } + ] +} +``` + +### FindPlan + +`FindPlan` is a class that contains the execution plan of a find operation. It has the following properties: + +- `byIdFilter`: It contains the filter for finding a document using `NitriteId`. +- `indexScanFilter`: It contains the filter for finding a document using index. +- `collectionScanFilter`: It contains the filter for finding a document using full scan. +- `indexDescriptor`: It contains the index descriptor for finding a document using index. +- `indexScanOrder`: It contains the sort order for finding a document using index. +- `blockingSortOrder`: It contains the sort order for finding a document using full scan. +- `skip`: It contains the number of documents to be skipped from the beginning of the result set. +- `limit`: It contains the maximum number of documents to be returned by the find operation. +- `distinct`: It specifies if the find operation returns distinct documents. +- `subPlans`: It contains the sub plans for finding a document using `or` filter. + +You can get the execution plan of a find operation using `findPlan` getter. It returns a `Future` object. + +```dart +var cursor = collection.find(where("firstName").eq("John")); + +var findPlan = await cursor.findPlan; +``` + + + diff --git a/flutter-sdk/database.md b/flutter-sdk/database.md index 49707c0..170f5d9 100644 --- a/flutter-sdk/database.md +++ b/flutter-sdk/database.md @@ -2,4 +2,170 @@ icon: database label: Database order: 19 ---- \ No newline at end of file +--- + +Nitrite database is a serverless, embedded, and self-contained database for Flutter. It is an open-source project that provides a simple API for persistent data storage. Nitrite database is designed to be lightweight, fast, and easy to use. + +## Creating a Database + +Nitrite database can be created in-memory or on-disk. By default, Nitrite database is created in-memory. To create a database on-disk, you need to add a storage module dependency to your project. More details about storage modules can be found [here](modules/store-modules/store-modules.md). + +To create a database, you need to use `NitriteBuilder` class. To get an instance of `NitriteBuilder`, you need to call `builder()` method on `Nitrite` class. + +```dart +var builder = Nitrite.builder(); +``` + +### In-memory Database + +If you don't load any on-disk storage module, then Nitrite will create an in-memory database. The below code snippet shows how to create a database in-memory. + +```dart +var db = await Nitrite.builder() + .openOrCreate(); +``` + +### On-disk Database + +The below code snippet shows how to create a database on-disk. + +#### Hive Backed Database + +```dart +var storeModule = HiveModule.withConfig() + .crashRecovery(true) + .path("/path/to/my.db") + .build(); + +var db = await Nitrite.builder() + .loadModule(storeModule) + .openOrCreate(); +``` + +More details about Hive configuration can be found [here](modules/store-modules/hive.md). + +## NitriteBuilder + +`NitriteBuilder` is the builder class for Nitrite database. It provides methods to configure and create a Nitrite database instance. + +### Open or Create a Database + +To open or create a database, you need to call `openOrCreate()` method on `NitriteBuilder` instance. This method returns a `Future` instance. + +If no `StoreModule` is configured, then Nitrite will create an in-memory database. If a `StoreModule` is configured, then Nitrite will create an on-disk database. If the database file does not exist, then Nitrite will create a new database file. If the database file exists, then Nitrite will open the database file. + +```dart +var db = await Nitrite.builder() + .loadModule(storeModule) + .openOrCreate(); +``` + +### Securing a Database + +To secure a database, you need to call `openOrCreate()` method with username and password on `NitriteBuilder` instance. This method returns a `Future` instance with the given username and password. + +```dart +var db = await Nitrite.builder() + .loadModule(storeModule) + .openOrCreate(username: "user", password: "password"); +``` + +!!!warning +If you are using a file-based database, then you need to use the same username and password to open the database again. Otherwise, you will get a `NitriteSecurityException`. + +Both username and password must be provided or both must be null. +!!! + +### Registering an EntityConverter + +Nitrite database uses a mapper to map a dart object to a document and vice versa. By default, Nitrite uses `SimpleNitriteMapper` as its mapper. This mapper uses `EntityConverter`s to map a dart object to a document and vice versa. To register an `EntityConverter`, you need to call `registerEntityConverter()` method on `NitriteBuilder` instance. This method returns the same `NitriteBuilder` instance. + +```dart +var db = await Nitrite.builder() + .loadModule(storeModule) + .registerEntityConverter(MyEntityConverter()) + .openOrCreate(); +``` + +More details about `EntityConverter` can be found [here](repository/entity.md#entityconverter). + +### Loading a Module + +Nitrite database is modular in nature. It provides various modules to extend its functionality. To load a module, you need to call `loadModule()` method on `NitriteBuilder` instance. This method returns the same `NitriteBuilder` instance. + +#### Loading a Storage Module + +```dart +var db = Nitrite.builder() + .loadModule(storeModule) + .openOrCreate(); +``` + +More on the Nitrite's module system can be found [here](modules/modules.md). + +### Adding Migration Steps + +Nitrite database supports schema migration. To configure a migration step, you need to call `addMigrations()` method on `NitriteBuilder` instance. This method returns the same `NitriteBuilder` instance. + +```dart +var migration = Migration( + initialSchemaVersion, + 2, + (instructionSet) { + instructionSet.forDatabase().addUser('test-user', 'test-password'); + + instructionSet + .forRepository(key: 'demo1') + .renameRepository('new') + .changeDataType('empId', (value) => int.parse(value)) + .changeIdField(Fields.withNames(['uuid']), Fields.withNames(['empId'])) + .deleteField('uuid') + .renameField('lastName', 'familyName') + .addField('fullName', generator: (document) => + '${document['firstName']} ${document['familyName']}', + ) + .dropIndex(['firstName']) + .dropIndex(['literature.text']) + .changeDataType('literature.ratings', (value) => (value as double).round()); + }, +); + +var db = await Nitrite.builder() + .loadModule(storeModule) + .addMigrations([migration]) + .openOrCreate(); +``` + +More on the schema migration can be found [here](migration.md). + +### Current Schema Version + +To get the current schema version of a database, you need to call `schemaVersion()` method on `NitriteBuilder` instance. This method returns the current schema version of the database. + +```dart +var db = await Nitrite.builder() + .loadModule(storeModule) + .schemaVersion(2); + .openOrCreate(); +``` + +!!!info +By default, the initial schema version is set to `1`. +!!! + +### Field Separator Character + +To configure the field separator character, you need to call `fieldSeparator()` method on `NitriteBuilder` instance. This method returns the same `NitriteBuilder` instance. + +It is used to separate field names in a nested document. For example, if a document has a field address which is a nested document, then the field street of the nested document can be accessed using address.street syntax. + +```dart +var db = await Nitrite.builder() + .loadModule(storeModule) + .fieldSeparator('.') + .openOrCreate(); +``` + +!!!info +The default field separator character is set to `.`. +!!! \ No newline at end of file diff --git a/flutter-sdk/document.md b/flutter-sdk/document.md index 29214b0..f27448d 100644 --- a/flutter-sdk/document.md +++ b/flutter-sdk/document.md @@ -2,4 +2,240 @@ label: Document icon: project-roadmap order: 18 ---- \ No newline at end of file +--- + +Document is the basic unit of data in Nitrite database. It is a JSON like field-value pairs. The field is always a `String` and value can be anything including `null`. Document is schema-less, which means you can store any kind of data in a document. + +Nitrite document supports nested document. That means, a value of a field can be another document. This allows you to create complex data structure. + +## Document Structure + +Nitrite document has the following structure. + +```json +{ + "firstName": "John", + "lastName": "Doe", + "address": { + "street": "123 Main Street", + "city": "New York", + "state": "NY", + "zip": "10001" + }, + "phone": [ + "212-555-1234", + "646-555-4567" + ] +} +``` + +## Document Field + +Document field is always a `String`. It can be any valid string. The field cannot be `null` or empty string. + +Below fields are reserved and cannot be used as key in a document. + +- *_id* : The unique identifier of a document. This field is auto-generated by Nitrite database during insertion. +- *_revision* : The revision number of a document. This field is auto-generated by Nitrite database during insertion and update. +- *_source* : The source collection name of a document. +- *_modified* : The last modified timestamp of a document. This field is auto-generated by Nitrite database during insertion and update. + +## Document Value + +Document value can be any valid dart object. It can be `null` or any of the below types. + +- `String` +- `int` +- `double` +- `bool` +- `List` +- `Map` +- `Set` +- `DateTime` +- `NitriteId` +- `Document` + +## Document Identifier + +It is a unique identifier of a document. It is auto-generated by Nitrite database during insertion. It is a `NitriteId` and is stored as a `String` in the document. + +### NitriteId + +NitriteId is a unique identifier across the Nitrite database. Each document in a nitrite collection is associated with a `NitriteId`. + +During insertion if a unique `String` value representing a integer is supplied in the `_id` field of the document, then the value of the `_id` field will be used to generate the `NitriteId`. Otherwise, a new `NitriteId` will be generated and will be used in the `_id` field of the document. + +The value of the `NitriteId` is a `String` representation of a 64-bit integer. The id generation is based on Twitter Snowflake algorithm. The id is composed of: + +- 41 bits for time in milliseconds +- 10 bits for a machine id +- 12 bits for a sequence number +- 1 unused sign bit + +The id is not guaranteed to be monotonically increasing. The id is sortable and the timestamp is stored in the id itself. + +#### Retrieving a NitriteId from a Document + +The id can be retrieved from a document using `Document.id` getter. If the document does not have an id, then it will create a new `NitriteId` and will set it in the document and will return the id. If the document already has an id, then it will return the id. + +```dart +var document = createDocument("firstName", "John"); + +var id = document.id; +``` + +## Field Separator + +To access a field of a nested document, or an element of an array field, you need to use the field separator. + +Field separator is configurable. You can change the field separator character by calling `NitriteBuilder.fieldSeparator()` method. + +```dart +var db = await nitriteBuilder() + .fieldSeparator('.') + .openOrCreate(); +``` + +!!!info +Nitrite uses `.` as field separator by default. +!!! + +### Nested Document + +To specify or access a field of a nested document, you need to use the field separator character. That means, if a document has a field address which is a nested document, then the field street of the nested document can be accessed using address.street syntax. + +### Array Field + +To specify or access an element of an array field, you need to use the index of the element. For example, if a document has a field phone which is an array, then the first element of the array can be accessed using phone.0 syntax. + +## Creating a Document + +To create a document, you need to use `createDocument()` function. + +### Creating an Empty Document + +```dart +var document = emptyDocument(); +``` + +### Creating a Document with Initial Field-value Pair + +```dart +var document = createDocument("firstName", "John"); +``` + +### Creating a Document with a Map + +```dart +var map = { + "firstName": "John", + "lastName": "Doe", + "address": { + "street": "123 Main Street", + "city": "New York", + "state": "NY", + "zip": "10001" + }, + "phone": [ + "212-555-1234", + "646-555-4567" + ] +}; + +var document = documentFromMap(map); +``` + +## Updating a Document + +To update a document, you need to use `Document.put()` method. This method takes two parameters, the field name and the value. If the field already exists in the document, then the value will be updated. If the field does not exist, then it will be created. + +You can also use the `[]` operator to update a document. + +```dart +var document = createDocument("firstName", "John"); + +document.put("lastName", "Doe"); +document["address"] = createDocument("street", "123 Main Street"); +``` + +## Retrieving a Value from Document + +To retrieve a value from document, you need to use `Document.get()` method. This method takes one parameter, the field name. If the field exists in the document, then the value will be returned. If the field does not exist, then it will return `null`. + +You can also use the `[]` operator to retrieve a value from a document. + +```dart +var document = createDocument("firstName", "John"); + +var firstName = document.get("firstName"); +var firstName = document["firstName"]; +``` + +To retrieve a value from a nested document, use the field separator character. + +```dart +var document = createDocument("firstName", "John") + .put("address", createDocument("street", "123 Main Street")); + +var street = document.get("address.street"); +``` + +To retrieve an element from an array field, use the index of the element. + +```dart +var document = createDocument("firstName", "John") + .put("phone", ["212-555-1234", "646-555-4567"]); + +var phone = document.get("phone.0"); +``` + +## Removing a Field from Document + +To remove a field from document, you need to use `Document.remove()` method. This method takes one parameter, the field name. If the field exists in the document, then it will be removed. If the field does not exist, then it will do nothing. + +```dart +var document = createDocument("firstName", "John"); + +document.remove("firstName"); +``` + +To remove a field from a nested document, use the field separator character. + +```dart +var document = createDocument("firstName", "John") + .put("address", createDocument("street", "123 Main Street")); + +document.remove("address.street"); +``` + +To remove an element from an array field, use the index of the element. + +```dart +var document = createDocument("firstName", "John") + .put("phone", ["212-555-1234", "646-555-4567"]); + +document.remove("phone.0"); +``` + +## Checking If a Field Exists in Document + +To check if a field exists in a document, you need to use `Document.containsKey()` method. This method takes one parameter, the field name. If the field exists in the document, then it will return `true`. If the field does not exist, then it will return `false`. + +```dart +var document = createDocument("firstName", "John"); + +var exists = document.containsKey("firstName"); +``` + +To check if a field exists in a nested document, use the field separator character. + +```dart +var document = createDocument("firstName", "John") + .put("address", createDocument("street", "123 Main Street")); + +var exists = document.containsKey("address.street"); +``` + +!!!warning +It cannot check if an element exists in an array field. +!!! \ No newline at end of file diff --git a/flutter-sdk/getting-started.md b/flutter-sdk/getting-started.md index 22d5d2b..caef8f9 100644 --- a/flutter-sdk/getting-started.md +++ b/flutter-sdk/getting-started.md @@ -18,4 +18,23 @@ dependencies: nitrite: ^[latest version] ``` -The latest released version of Nitrite can be found [here](https://pub.dev/packages/nitrite). \ No newline at end of file +The latest released version of Nitrite can be found [here](https://pub.dev/packages/nitrite). + +To add the nitrite entity generator to your project, add the following to your `pubspec.yaml` file: + +```yaml +dev_dependencies: + build_runner: ^2.4.6 + nitrite_entity_generator: ^[latest version] +``` + +More details about the entity generator can be found [here](repository/entity.md#code-generator). + +To add the hive adapter to your project, add the following to your `pubspec.yaml` file: + +```yaml +dependencies: + nitrite_hive_adapter: ^[latest version] +``` + +More details about the hive adapter can be found [here](modules/store-modules/hive.md). \ No newline at end of file diff --git a/flutter-sdk/repository.md b/flutter-sdk/repository.md deleted file mode 100644 index bb48072..0000000 --- a/flutter-sdk/repository.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -label: Object Repository -icon: container -order: 16 ---- \ No newline at end of file diff --git a/flutter-sdk/repository/entity.md b/flutter-sdk/repository/entity.md new file mode 100644 index 0000000..e69de29 diff --git a/java-sdk/collection/read.md b/java-sdk/collection/read.md index b296178..25032b0 100644 --- a/java-sdk/collection/read.md +++ b/java-sdk/collection/read.md @@ -115,7 +115,7 @@ long size = cursor.size(); ### Projection -You can project the result set using `project()` method. It takes a `Document` as the only input parameter. It returns a `DocumentCursor` object. +You can project the result set using `project()` method. It takes a `Document` as the only input parameter. It returns a `RecordStream` object. The document must contain only the fields that needs to be projected. The field values must be `null` or a nested document. The condition holds true for nested documents as well. @@ -211,7 +211,7 @@ And the `orders` collection contains the following documents: } ``` -Now, you want to join these two collections using `userId` field. Then you can do it like this: +Now, you want to join these two collections on `userId` field. Then you can do it like this: ```java DocumentCursor users = db.getCollection("users").find(); diff --git a/java-sdk/document.md b/java-sdk/document.md index 11f7a33..0114bdf 100644 --- a/java-sdk/document.md +++ b/java-sdk/document.md @@ -165,7 +165,7 @@ String phone = document.get("phone.0", String.class); String phone = (String) document.get("phone.0"); ``` -## Removing a field from document +## Removing a Field from Document To remove a field from document, you need to use `Document.remove()` method. This method takes one parameter, the field name. If the field exists in the document, then it will be removed. If the field does not exist, then it will do nothing. diff --git a/retype.yml b/retype.yml index b5102e7..7082181 100644 --- a/retype.yml +++ b/retype.yml @@ -12,7 +12,7 @@ favicon: /assets/favicon.png links: - text: Home icon: home - link: https://nitrite.dizitart.com + link: https://nitrite.github.io - text: Github link: https://github.com/nitrite diff --git a/welcome.md b/welcome.md index 6346719..3e79d75 100644 --- a/welcome.md +++ b/welcome.md @@ -3,6 +3,7 @@ icon: home label: Welcome description: 'Nitrite is a serverless, embedded, and self-contained NoSQL database. It is an open-source project that provides a simple API for persistent data storage. Nitrite database is designed to be lightweight, fast, and easy to use.' order: 10 +layout: blog --- ![](assets/banner.svg)