【Local DB】Introducing Isar Class Definitions and Data Manipulation Methods!!

Previously, I posted a basic article about Isar.

This time, I will introduce more detailed Isar class definitions and data manipulation methods.
I hope it will be helpful for your development!

Preparation

When using Isar, the following preparations are necessary.

Setting up pubspec.yaml

// Add the following packages.
dependencies:
  isar:
  isar_flutter_libs:

dev_dependencies:
  build_runner:
  isar_generator:

Creating an Isar Instance

import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';

// When using Riverpod
import 'package:flutter_riverpod/flutter_riverpod.dart';
/// Location for storing custom Isar providers
import 'package:myApp/email_repository.dart';


// Create within main
Future<void> main() async {
  // Specify the location to save the local DB
  final dir = await getApplicationSupportDirectory();

  // Create an Isar instance
  final isar = await Isar.open([EmailSchema], directory: dir.path);

  // Create an Isar instance
  runApp(ProviderScope(
    overrides: [
      emailIsarProvider.overrideWithValue(isar),
    ],
    child: const MyApp(),
  ));


// For reference, if using multiple collections
Future<void> main() async {
  // Specify the location to save the local DB
  final dir = await getApplicationSupportDirectory();

  // Create an Isar instance
  final isar = await Isar.open([UserSchema, EmailSchema], directory: dir.path);

  // Create an Isar instance
  runApp(ProviderScope(
    overrides: [
      userIsarProvider.overrideWithValue(isar),
      emailIsarProvider.overrideWithValue(isar),
    ],
    child: const MyApp(),
  ));
}

Defining Collections

In NoSQL, data is managed in collections and documents.
In relational databases, the relationship is as follows:

Table: Collection
Record: Document

To define a collection in Isar, you need to define the data structure as a class.

Basic Collection Definition

In Isar, data classes are defined using the @collection annotation.
Here is an example of defining a collection named Email.

@collection
class Email {
  Email {
    Id? id;
    required title;
    required updatedAt;
  }
  Id? id; 
  final String title;
  @Index()  // "@Index()" creates an index for the field below it
  final DateTime updatedAt;
}

This class has three fields: id, title, and updatedAt.
The id field is used to uniquely identify each entry in the database.

The id is automatically assigned by Isar.
When adding title and updatedAt to the IsarDB, the id is automatically assigned.

Data storage image:

{
  "id": 1234,  // Automatically assigned id
  "title": "How to use Isar!!"
  "updatedAt": yyyy-mm-dd hh:mm
}

Defining Hierarchical Collections

Sometimes you may want to structure documents in a hierarchical manner.
In such cases, define the child class using the @embedded annotation.

@collection
class Email {
  Email {
    Id? id;
    required title;
    required updatedAt;
    required recipient;
  }
  Id? id; 
  final String title;
  @Index()
  final DateTime updatedAt;
  final Recipient? recipient;
}

@embedded
class Recipient {
  String? name;
  String? address;
}

In this structure, the Recipient class is nested within the Email class.

Data image:

{
  "id": 99999,
  "title": "How to use Isar!!",
  "recipient": {
    "name": "John Doe",
    "address": "john.doe@gmail.com"
  }
}

After defining the class, you need to run build_runner to create the ~.g.dart.
Run the following commands in the terminal:

// Clean the cache
flutter pub run build_runner clean
// Run code generation
flutter pub run build_runner build --delete-conflicting-outputs

Writing Queries for Registration, Update, and Deletion

Next, let’s look at CRUD operations using Isar.

Single Data Registration/Update

To add new data to the database, create an instance from the collection definition and save it to the database using “put“.
If data with the same id exists, it will be updated; if it does not exist, it will be registered.

final Isar _isar; // Omitted below

final email = Email()
  ..title = "How to use Isar CRUD!!"
  ..recipient = Recipient()
      ..name = "Jane Doe"
      ..address = "jane.doe@gmail.com";

await _isar.emails.put(email);
// "emails" is the collection class defined in the generated file (in this case, email_dto.g.dart)

Batch Data Registration/Update

When dealing with multiple data at once, use the writeTxn method for transaction processing.
To register or update multiple data, use “putAll“.

await isar.writeTxn(() async {
  await isar.emails.putAll([email1, email2]);
});

Single Data Deletion

To delete data, specify the id of the data to be deleted using “delete“.

await isar.emails.delete(emailId);

Batch Data Deletion

To delete multiple data that match certain conditions, use “deleteAll“.

await isar.emails.deleteAll(emailIds);

Writing Search Queries

Next, let’s look at useful methods for searching data within the database.

Where Clause

To find data that meets specific conditions, use “where“.
However, the fields that can use where must have an index (with @Index()).

When using where, conditions are placed between “where()” and “findAll()”.

// Search for documents where updatedAt matches the specified time
final results = isar.emails
    .where()
    .updatedAtEqualTo("2024-12-31 23:59")
    .findAll();

The following conditions can be used in the where clause.

Using Filter Clauses

To set more complex conditions, use “filter“.
Filters can be used on fields without an index, but they are slower than where.
(Probably because the data is retrieved first and then filtered.)

When using filter, conditions are placed between “filter()” and “findAll()”.

// Search for documents where the title starts with "How" (case insensitive)
final filteredResults = isar.emails
    .filter()
    .titleStartsWith("How", caseSensitive: false)
    .findAll();

Filter Conditions

Besides the above, various conditions can be used.
These are created in the ~.g.dart.

Logical Operators such as And and Or

Use And and Or to specify multiple conditions.

// Search for documents where the title starts with "How" and updatedAt matches the specified time
final filteredResults = isar.emails
    .filter()
    .titleStartsWith("How")
    .and()
    .updatedAtEqualTo("2024-12-31 23:59")
    .findAll();

Other operators commonly used in SQL can also be used.

Combining Where and Filter Clauses

Typically, queries are created by combining Where and Filter clauses.

// Search for documents where the title is greater than "How" and updatedAt is not null
final filteredResults = isar.emails
    .titleGreaterThan("How")
    .filter()
    .updatedAtIsNotNull()
    .findAll();

Since Where is faster, try to narrow down the target as much as possible with Where and then specify complex conditions with Filter.

Offset and Limit

To get a subset of results by the number of records rather than the content, set “offset” and “limit“.
Offset specifies the number of records not to retrieve from the start, and limit specifies the number of records to retrieve.

final paginatedResults = isar.emails
    .where()
    .findAll(offset: 10, limit: 5);  // Retrieve 5 records starting from the 11th

Search Entire Database

To retrieve all data in the database, use “findAll” alone.

final allEmails = await isar.emails
    .findAll();

Conclusion

I have explained the basic methods for efficiently managing data using Flutter and Isar.
There are still many useful features. I will write articles about the features that I could not introduce this time in the future.

There are also usage instructions on the official website, so please refer to them as well!!
https://isar.dev/crud.html

Copied title and URL