tostore 2.1.2 copy "tostore: ^2.1.2" to clipboard
tostore: ^2.1.2 copied to clipboard

A advanced storage engine that supports relational and NoSQL database,data smart cache, multi-space architecture,file/local storage, SQL & key-value persistent store.

example/lib/main.dart

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:tostore/tostore.dart';

/// This example demonstrates the core features of Tostore using a user management system
/// with global settings. It shows how to:
/// - Create tables (both regular and global)
/// - Work with multi-space architecture
/// - Distributed example
/// - Handle global data
class TostoreExample {
  late ToStore db;

  /// Initialize database and create tables
  Future<void> initialize() async {
    db = ToStore(
      schemas: [
        // suitable for table structure definition in frequent startup scenarios of mobile applications, accurately identifying table structure changes, automatically upgrading and migrating data
        const TableSchema(
          name: 'users',
          primaryKeyConfig: PrimaryKeyConfig(
            name: 'id',
          ),
          fields: [
            FieldSchema(name: 'username', type: DataType.text, nullable: false),
            FieldSchema(name: 'email', type: DataType.text, nullable: false),
            FieldSchema(name: 'last_login', type: DataType.datetime),
            FieldSchema(
                name: 'is_active', type: DataType.boolean, defaultValue: true),
            FieldSchema(name: 'age', type: DataType.integer),
            FieldSchema(name: 'tags', type: DataType.text),
            FieldSchema(name: 'type', type: DataType.text),
            FieldSchema(name: 'fans', type: DataType.integer, defaultValue: 0),
          ],
          indexes: [
            IndexSchema(fields: ['username'], unique: true),
            IndexSchema(fields: ['email'], unique: true),
            IndexSchema(fields: ['last_login'], unique: false),
            IndexSchema(fields: ['is_active']),
            IndexSchema(fields: ['type']),
          ],
        ),
        const TableSchema(
          name: 'posts',
          primaryKeyConfig: PrimaryKeyConfig(
            name: 'id',
          ),
          fields: [
            FieldSchema(name: 'title', type: DataType.text, nullable: false),
            FieldSchema(name: 'content', type: DataType.text),
            FieldSchema(
                name: 'user_id', type: DataType.integer, nullable: false),
            FieldSchema(name: 'created_at', type: DataType.datetime),
            FieldSchema(
                name: 'is_published',
                type: DataType.boolean,
                defaultValue: true),
          ],
          indexes: [
            IndexSchema(fields: ['user_id']),
            IndexSchema(fields: ['created_at']),
          ],
        ),
        const TableSchema(
          name: 'comments',
          primaryKeyConfig: PrimaryKeyConfig(
            name: 'id',
          ),
          fields: [
            FieldSchema(
                name: 'post_id', type: DataType.integer, nullable: false),
            FieldSchema(
                name: 'user_id', type: DataType.integer, nullable: false),
            FieldSchema(name: 'content', type: DataType.text, nullable: false),
            FieldSchema(name: 'created_at', type: DataType.datetime),
          ],
          indexes: [
            IndexSchema(fields: ['post_id']),
            IndexSchema(fields: ['user_id']),
          ],
        ),
        const TableSchema(
          name: 'settings',
          primaryKeyConfig: PrimaryKeyConfig(),
          isGlobal: true,
          fields: [
            FieldSchema(
                name: 'key',
                type: DataType.text,
                nullable: false,
                unique: true),
            FieldSchema(name: 'value', type: DataType.text),
            FieldSchema(name: 'updated_at', type: DataType.datetime),
          ],
          indexes: [
            IndexSchema(fields: ['key'], unique: true),
            IndexSchema(fields: ['updated_at'], unique: false),
          ],
        ),
      ],
    );
    await db.initialize();
  }

  /// Example: Basic CRUD operations for users
  Future<void> userExamples() async {
    // Create: Insert a new user
    await db.insert('users', {
      'username': 'john_doe',
      'email': 'john@example.com',
      'last_login': DateTime.now().toIso8601String(),
    });

    // Read: Query user using chain style
    await db.query('users').where('username', '=', 'john_doe');

    // Update: Modify user data
    await db.update('users', {
      'last_login': DateTime.now().toIso8601String(),
    }).where('username', '=', 'john_doe');

    // Automatically store data,Support batch upsert
    await db.upsert('users', {'name': 'John'}).where(
        'email', '=', 'john@example.com');

    // Auto insert or update based on primary key
    await db.upsert(
        'users', {'id': 1, 'name': 'John', 'email': 'john@example.com'});

    // Delete: Remove user
    await db.delete('users').where('username', '=', 'john_doe');

    // use stream query to handle large data
    db
        .streamQuery('users')
        .where('email', 'like', '%@example.com')
        .listen((userData) {
      // handle each data as needed, avoid memory pressure
      log('handle user: ${userData['username']}');
    });
  }

  /// Example: Working with global settings
  Future<void> settingsExamples() async {
    // Set global theme (accessible from any space)
    await db.insert('settings', {
      'key': 'theme',
      'value': 'dark',
      'updated_at': DateTime.now().toIso8601String(),
    });

    // Alternative: Using setValue for simple key-value storage
    await db.setValue('language', 'en', isGlobal: true);

    // Read settings using different methods
    await db.query('settings').where('key', '=', 'theme');

    await db.getValue('language', isGlobal: true);

    // Update setting with conflict resolution
    await db.update('settings', {
      'value': 'light',
      'updated_at': DateTime.now().toIso8601String(),
    }).where('key', '=', 'theme');

    // Delete setting
    await db.delete('settings').where('key', '=', 'theme');
  }

  /// Example: Multi-space feature for user data isolation
  Future<void> multiSpaceExamples() async {
    // Switch to user1's space
    await db.switchSpace(spaceName: 'user1');
    await db.insert('users', {
      'username': 'user1',
      'email': 'user1@example.com',
      'last_login': DateTime.now().toIso8601String(),
    });

    // Switch to user2's space
    await db.switchSpace(spaceName: 'user2');
    await db.insert('users', {
      'username': 'user2',
      'email': 'user2@example.com',
      'last_login': DateTime.now().toIso8601String(),
    });

    // Global settings remain accessible in any space
    await db.getValue('theme', isGlobal: true);

    // get current space info
    final spaceInfo = await db.getSpaceInfo();
    log("${spaceInfo.toJson()}");
  }

  /// Example: Advanced queries
  Future<void> advancedQueryExamples() async {
    // Complex conditions
    await db
        .query('users')
        .where(
            'last_login',
            '>',
            DateTime.now()
                .subtract(
                  const Duration(days: 7),
                )
                .toIso8601String())
        .or()
        .where('email', 'LIKE', '%@example.com')
        .orderByDesc('last_login')
        .limit(10);

    // Count users
    await db.query('users').count();

    // Batch operations
    await db.batchInsert('users', [
      {
        'username': 'user3',
        'email': 'user3@example.com',
        'last_login': DateTime.now().toIso8601String(),
      },
      {
        'username': 'user4',
        'email': 'user4@example.com',
        'last_login': DateTime.now().toIso8601String(),
      },
    ]);
  }

  /// Example: Backup and restore
  Future<void> backupExample() async {
    // Create backup
    final backupPath = await db.backup(compress: false);
    log('Backup created at: $backupPath');

    // Restore from backup
    await db.restore(backupPath, deleteAfterRestore: true);
  }

  /// Example: Working with vector data
  Future<void> vectorExamples() async {
    // create table structure
    await db.createTables([
      const TableSchema(
        name: 'embeddings',
        primaryKeyConfig: PrimaryKeyConfig(
          name: 'id',
          type: PrimaryKeyType.timestampBased,
        ),
        fields: [
          FieldSchema(
            name: 'document_title',
            type: DataType.text,
            nullable: false,
          ),
          FieldSchema(
            name: 'embedding',
            type: DataType.vector,
            vectorConfig: VectorFieldConfig(
              dimensions: 1536, // 1536 dimensions
              precision: VectorPrecision.float64, // 64-bit precision
            ),
          ),
        ],
        indexes: [
          IndexSchema(
            fields: ['embedding'],
            type: IndexType.vector,
            vectorConfig: VectorIndexConfig(
              indexType:
                  VectorIndexType.hnsw, // HNSW for fast approximate search
              distanceMetric: VectorDistanceMetric.cosine, // Cosine similarity
              parameters: {
                'M': 16, // Max number of connections per layer
                'efConstruction': 200, // Controls index quality
                'efSearch': 100, // Controls search accuracy/speed trade-off
              },
            ),
          ),
        ],
      ),
    ]);

    // create vector data
    final sampleVector1 = VectorData.fromList([0.1, 0.2, 0.3, 0.4]);
    final sampleVector2 = VectorData.fromList([0.5, 0.6, 0.7, 0.8]);

    // store documents with vector embeddings
    await db.insert('embeddings', {
      'document_title': 'Introduction to vector databases',
      'embedding': sampleVector1,
    });

    await db.insert('embeddings', {
      'document_title': 'Machine Learning with embeddings',
      'embedding': sampleVector2,
    });

    // query stored vector data
    final result = await db.query('embeddings');
    final documents = result.data;
    List<VectorData> vectors = [];

    for (var doc in documents) {
      if (doc['embedding'] is List) {
        VectorData vector =
            VectorData.fromJson(doc['embedding'] as List<dynamic>);
        vectors.add(vector);
        doc['embedding'] = vector;

        log('document title: ${doc['document_title']}, vector dimensions: ${vector.dimensions}');
      }
    }

    // at least two vectors can be compared
    if (vectors.length >= 2) {
      final vector1 = vectors[0];
      final vector2 = vectors[1];

      log('vector1: ${vector1.toString()}');
      log('vector2: ${vector2.toString()}');

      // calculate vector similarity
      final similarity = vector1.cosineSimilarity(vector2);
      log('vector similarity: $similarity');

      // calculate euclidean distance
      final distance = vector1.euclideanDistance(vector2);
      log('vector distance: $distance');
    } else {
      log('not enough vector data for comparison');
    }
  }

  /// backend server or distributed example
  Future<void> distributedExample() async {
    // create database instance
    final db = ToStore(
      config: DataStoreConfig(
        enableEncoding: true, // enable security encoding for table data
        encodingKey: 'YouEncodingKey', // encoding key, can be adjusted
        encryptionKey:
            'YouEncryptionKey', // encryption key, note: adjusting this key will make it impossible to decode old data
        distributedNodeConfig: const DistributedNodeConfig(
          enableDistributed: true, // enable distributed mode
          clusterId: 1, // configure cluster id
          centralServerUrl: 'http://127.0.0.1:8080',
          accessToken: 'b7628a4f9b4d269b98649129',
        ),
        enableLog: true, // enable log
        logLevel: LogLevel.warn, // log level
      ),
    );

    // create tables
    await db.createTables(
      [
        const TableSchema(
            name: 'users',
            primaryKeyConfig: PrimaryKeyConfig(
              name: 'id',
              type: PrimaryKeyType.sequential, // sequential key type
              sequentialConfig: SequentialIdConfig(
                initialValue: 10000, // initial value
                increment: 50, // increment
                useRandomIncrement:
                    true, // use random increment, avoid exposing business volume
              ),
            ),
            // field and index definition ...
            fields: []),
      ],
      // other tables ...
    );

    // update table structure
    final taskId = await db
        .updateSchema('users')
        .renameTable('newTableName') // rename table
        .modifyField('username',
            minLength: 5,
            maxLength: 20,
            unique: true) // modify field attributes
        .renameField('oldName', 'newName') // rename field
        .removeField('fieldName') // remove field
        .addField('name', type: DataType.text) // add field
        .removeIndex(fields: ['age']) // remove index
        .setPrimaryKeyConfig(// set primary key config
            const PrimaryKeyConfig(type: PrimaryKeyType.shortCode));

    // query migration task status
    final status = await db.queryMigrationTaskStatus(taskId);
    log('migration progress: ${status?.progressPercentage}%');
  }

  /// Example: Complex nested queries with predefined conditions
  Future<void> complexQueryExamples() async {
    // prepare some test data
    await db.insert('users', {
      'username': 'active_user',
      'email': 'active@example.com',
      'is_active': true,
      'age': 25,
      'type': 'app',
      'fans': 300,
      'tags': 'recommend,hot,featured',
      'last_login': DateTime.now().toIso8601String(),
    });

    await db.insert('users', {
      'username': 'inactive_user',
      'email': 'inactive@example.com',
      'is_active': false,
      'age': 30,
      'type': 'web',
      'fans': 150,
      'tags': 'normal,newbie',
      'last_login':
          DateTime.now().subtract(const Duration(days: 30)).toIso8601String(),
    });

    // complex query condition nesting - pre-defined query condition module
    final recentLoginCondition = QueryCondition().where('fans', '>=', 200);

    final idCondition = QueryCondition()
        .where('id', '>=', 1)
        .orCondition(// orCondition is equivalent to OR condition combination
            recentLoginCondition);

    // custom condition function - flexible handling of any complex logic
    final customCondition = QueryCondition().whereCustom((record) {
      // for example: check if the tag contains 'recommend'
      return record['tags'] != null &&
          record['tags'].toString().contains('recommend');
    });

    // query condition nesting example - show infinite nesting ability
    final result = await db
        .query('users')
        .where('is_active', '=', true)
        .condition(QueryCondition() // query condition construction
                .whereEqual('type', 'app')
                .condition(idCondition) // nest again the defined conditions
            )
        .orCondition(customCondition) // or satisfy custom complex conditions
        .limit(20);

    for (var user in result.data) {
      log('user: ${user['username']}, type: ${user['type']}, fans: ${user['fans']}, tags: ${user['tags']}');
    }

    // equivalent SQL:
    // SELECT * FROM users
    // WHERE is_active = true
    //   AND (type = 'app' OR id >= 1 OR fans >= 200)
    //   OR ([custom condition: tag contains 'recommend'])
    // LIMIT 20
  }

  /// Example: Join queries with table relationships
  Future<void> joinQueryExamples() async {
    // prepare test data
    // insert user
    await db.insert('users', {
      'username': 'blogger',
      'email': 'blogger@example.com',
      'is_active': true,
    });

    // insert post
    await db.insert('posts', {
      'title': 'how to use join to query',
      'content': 'this is a post about join...',
      'user_id': 1,
      'created_at': DateTime.now().toIso8601String(),
    });

    // insert comment
    await db.insert('comments', {
      'post_id': 1,
      'user_id': 1,
      'content': 'this is my own post comment',
      'created_at': DateTime.now().toIso8601String(),
    });

    // multi-table join query - post, author and comment
    final postsWithComments = await db
        .query('posts')
        .select([
          'posts.id as post_id',
          'posts.title',
          'users.username as author',
          'comments.content as comment',
          'comments.created_at as comment_time'
        ])
        .join('users', 'posts.user_id', '=', 'users.id')
        .join('comments', 'posts.user_id', '=', 'comments.user_id')
        .where('posts.is_published', '=', true)
        .orderByDesc('comments.created_at');

    for (var item in postsWithComments.data) {
      log('post: ${item['title']}, author: ${item['author']}, comment: ${item['comment']}');
    }
  }
}

/// Simple UI to run examples
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final example = TostoreExample();
  await example.initialize();

  runApp(const MaterialApp(
    home: Scaffold(
      body: Center(
        child: Text('Check console for example outputs'),
      ),
    ),
  ));
}
30
likes
160
points
146k
downloads

Publisher

verified publishertoway.world

Weekly Downloads

A advanced storage engine that supports relational and NoSQL database,data smart cache, multi-space architecture,file/local storage, SQL & key-value persistent store.

Repository (GitHub)
View/report issues
Contributing

Topics

#database #storage #sql #nosql #distributed

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

archive, flutter, path, path_provider, web

More

Packages that depend on tostore