zvec 0.5.2 copy "zvec: ^0.5.2" to clipboard
zvec: ^0.5.2 copied to clipboard

Dart SDK for Zvec — a lightweight, lightning-fast, in-process vector database by Alibaba.

example/lib/main.dart

import 'dart:io';
import 'dart:math';
import 'dart:typed_data';

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

Future<void> main() async {
  if (const bool.fromEnvironment('ZVEC_RELEASE_SMOKE')) {
    await _runReleaseSmoke();
    return;
  }

  runApp(const ZvecDemoApp());
}

Future<void> _runReleaseSmoke() async {
  var initialized = false;
  final dir = Directory.systemTemp.createTempSync('zvec_release_app_');

  try {
    stdout.writeln('Starting zvec release smoke test');
    Zvec.initialize();
    initialized = true;

    _runReleaseVectorSmoke(dir);
    _runReleaseJiebaSmoke(dir);

    stdout.writeln('zvec release smoke test passed');
    exit(0);
  } catch (error, stackTrace) {
    stderr.writeln('zvec release smoke test failed: $error');
    stderr.writeln(stackTrace);
    exit(1);
  } finally {
    if (initialized) {
      Zvec.shutdown();
    }
    if (dir.existsSync()) {
      dir.deleteSync(recursive: true);
    }
  }
}

String _smokeDbPath(Directory dir, String name) {
  return '${dir.path}${Platform.pathSeparator}$name';
}

void _checkSmoke(bool condition, String message) {
  if (!condition) {
    throw StateError(message);
  }
}

void _runReleaseVectorSmoke(Directory dir) {
  Collection? collection;
  CollectionSchema? schema;
  VectorQuery? query;

  try {
    schema = CollectionSchema(
      name: 'release_vector_smoke',
      fields: [
        VectorSchema('embedding', 4, indexParams: FlatIndexParams()),
        FieldSchema(name: 'title', dataType: DataType.string),
      ],
    );

    collection = Collection.createAndOpen(
      _smokeDbPath(dir, 'vector_db'),
      schema,
    );
    schema.destroy();
    schema = null;

    final docs = [
      Doc(id: 'nearest')
        ..setField('title', 'Nearest')
        ..setVector('embedding', Float32List.fromList([1, 0, 0, 0])),
      Doc(id: 'far')
        ..setField('title', 'Far')
        ..setVector('embedding', Float32List.fromList([0, 0, 0, 1])),
    ];

    final insertResult = collection.insert(docs);
    for (final doc in docs) {
      doc.destroy();
    }
    _checkSmoke(
      insertResult.successCount == docs.length && insertResult.errorCount == 0,
      'Vector insert failed: $insertResult',
    );

    collection.optimize();

    query = VectorQuery(
      fieldName: 'embedding',
      vector: Float32List.fromList([1, 0, 0, 0]),
      topk: 1,
      outputFields: ['title'],
    );
    final results = collection.query(query);

    _checkSmoke(results.length == 1, 'Expected one vector result');
    _checkSmoke(results.single.pk == 'nearest', 'Unexpected vector result');
    _checkSmoke(
      results.single.getString('title') == 'Nearest',
      'Unexpected vector result title',
    );
  } finally {
    query?.destroy();
    schema?.destroy();
    collection?.close();
  }
}

void _runReleaseJiebaSmoke(Directory dir) {
  final dictDir = Zvec.defaultJiebaDictDir;
  _checkSmoke(dictDir != null, 'Default Jieba dict dir was not registered');
  _checkSmoke(
    File('$dictDir${Platform.pathSeparator}jieba.dict.utf8').existsSync(),
    'Missing packaged jieba.dict.utf8',
  );
  _checkSmoke(
    File('$dictDir${Platform.pathSeparator}hmm_model.utf8').existsSync(),
    'Missing packaged hmm_model.utf8',
  );

  Collection? collection;
  CollectionSchema? schema;
  FieldSchema? title;
  FieldSchema? content;
  FtsIndexParams? ftsParams;
  FtsQuery? fts;
  VectorQuery? query;

  try {
    title = FieldSchema(
      name: 'title',
      dataType: DataType.string,
      nullable: false,
    );
    content = FieldSchema(
      name: 'content',
      dataType: DataType.string,
      nullable: false,
    );
    ftsParams = FtsIndexParams(tokenizerName: 'jieba', filters: ['lowercase']);
    content.setIndexParams(ftsParams);

    schema = CollectionSchema(name: 'release_jieba_smoke');
    schema.addField(title);
    schema.addField(content);
    title.destroy();
    title = null;
    content.destroy();
    content = null;
    ftsParams.destroy();
    ftsParams = null;

    collection = Collection.createAndOpen(
      _smokeDbPath(dir, 'jieba_db'),
      schema,
    );
    schema.destroy();
    schema = null;

    final docs = [
      Doc(id: 'pk_match')
        ..setField('title', 'match')
        ..setField('content', '中华人民共和国成立'),
      Doc(id: 'pk_miss')
        ..setField('title', 'miss')
        ..setField('content', '无关文档'),
    ];
    final insertResult = collection.insert(docs);
    for (final doc in docs) {
      doc.destroy();
    }
    _checkSmoke(
      insertResult.isAllSuccess,
      'Jieba insert failed: $insertResult',
    );

    fts = FtsQuery(matchString: '中华');
    query = VectorQuery.fts(
      fieldName: 'content',
      fts: fts,
      topk: 10,
      outputFields: ['title', 'content'],
    );
    final results = collection.query(query);

    final ids = results.map((doc) => doc.pk).toSet();
    _checkSmoke(ids.contains('pk_match'), 'Jieba FTS did not match pk_match');
    _checkSmoke(!ids.contains('pk_miss'), 'Jieba FTS matched pk_miss');
  } finally {
    query?.destroy();
    fts?.destroy();
    schema?.destroy();
    title?.destroy();
    content?.destroy();
    ftsParams?.destroy();
    collection?.close();
  }
}

class ZvecDemoApp extends StatelessWidget {
  const ZvecDemoApp({super.key, this.autoRun = true});

  final bool autoRun;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Zvec Demo',
      theme: ThemeData(colorSchemeSeed: Colors.blue, useMaterial3: true),
      home: DemoPage(autoRun: autoRun),
    );
  }
}

class DemoPage extends StatefulWidget {
  const DemoPage({super.key, this.autoRun = true});

  final bool autoRun;

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  final List<String> _logs = [];
  bool _running = false;

  @override
  void initState() {
    super.initState();
    if (widget.autoRun) {
      Future.delayed(const Duration(milliseconds: 500), _runDemo);
    }
  }

  void _log(String message) {
    setState(() => _logs.add(message));
  }

  Future<void> _runDemo() async {
    setState(() {
      _logs.clear();
      _running = true;
    });

    try {
      // 1. Initialize Zvec
      _log('Initializing Zvec...');
      Zvec.initialize();
      _log('Version: ${Zvec.version}');

      // 2. Create collection path
      final dir = await getApplicationDocumentsDirectory();
      final dbPath = '${dir.path}/zvec_demo';
      // Clean up previous run
      final dbDir = Directory(dbPath);
      if (dbDir.existsSync()) {
        dbDir.deleteSync(recursive: true);
      }

      // 3. Define schema: a 4-dim FP32 vector field + a string field.
      //    We create the collection WITHOUT an HNSW index first, insert data,
      //    then call optimize() which builds the index automatically.
      _log('Creating collection schema...');
      final schema = CollectionSchema(
        name: 'demo',
        fields: [
          VectorSchema('embedding', 4, indexParams: HnswIndexParams()),
          FieldSchema(name: 'title', dataType: DataType.string),
        ],
      );

      // 4. Create and open collection
      _log('Creating collection at: $dbPath');
      final collection = Collection.createAndOpen(dbPath, schema);

      // 5. Insert 10 documents with random vectors
      _log('Inserting 10 documents...');
      final rng = Random(42);
      final docs = <Doc>[];
      for (var i = 0; i < 10; i++) {
        final vec = Float32List.fromList(
          List.generate(4, (_) => rng.nextDouble()),
        );
        final doc = Doc(id: 'doc_$i')
          ..setField('title', 'Document #$i')
          ..setVector('embedding', vec);
        docs.add(doc);
      }
      collection.insert(docs);
      _log('Inserted ${docs.length} documents.');
      for (final doc in docs) {
        doc.destroy();
      }

      // 6. Optimize (build index)
      _log('Optimizing collection...');
      collection.optimize();

      // 7. Get stats
      final stats = collection.stats;
      _log(
        'Collection stats: ${stats.docCount} docs, '
        '${stats.indexCount} indexes',
      );
      stats.destroy();

      // 8. Vector search
      _log('Querying with a random vector...');
      final queryVec = Float32List.fromList(
        List.generate(4, (_) => rng.nextDouble()),
      );
      final query = VectorQuery(
        fieldName: 'embedding',
        vector: queryVec,
        topk: 5,
        outputFields: ['title'],
      );
      final results = collection.query(query);
      _log('Found ${results.length} results:');
      for (final doc in results) {
        final pk = doc.pk ?? '?';
        final title = doc.getString('title') ?? '?';
        final score = doc.score.toStringAsFixed(4);
        _log('  $pk: "$title" (score: $score)');
      }
      query.destroy();

      // 9. Fetch by primary key
      _log('Fetching doc_0 and doc_5...');
      final fetched = collection.fetch(['doc_0', 'doc_5']);
      for (final doc in fetched) {
        _log('  Fetched: ${doc.pk} - ${doc.getString('title')}');
      }

      // 10. Close collection
      collection.close();
      _log('Collection closed.');

      // 11. Shutdown
      Zvec.shutdown();
      _log('Zvec shutdown. Demo complete!');
    } catch (e, st) {
      _log('ERROR: $e');
      _log(st.toString().split('\n').take(5).join('\n'));
    } finally {
      setState(() => _running = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Zvec Demo')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: ElevatedButton(
              onPressed: _running ? null : _runDemo,
              child: Text(_running ? 'Running...' : 'Run Demo'),
            ),
          ),
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              itemCount: _logs.length,
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.symmetric(vertical: 2),
                  child: Text(
                    _logs[index],
                    style: const TextStyle(
                      fontFamily: 'monospace',
                      fontSize: 13,
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
3
likes
140
points
261
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Dart SDK for Zvec — a lightweight, lightning-fast, in-process vector database by Alibaba.

License

Apache-2.0 (license)

Dependencies

ffi, flutter, plugin_platform_interface

More

Packages that depend on zvec

Packages that implement zvec