├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── io
│ │ │ │ └── objectbox
│ │ │ │ └── dartbench
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── bin
└── objectbox.dart
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── lib
├── executor.dart
├── hive_executor.dart
├── isar_model.dart
├── isar_model.g.dart
├── isar_sync_executor.dart
├── main.dart
├── model.dart
├── model.g.dart
├── objectbox-model.json
├── objectbox.g.dart
├── obx_executor.dart
├── sqf_executor.dart
└── time_tracker.dart
├── linux
├── .gitignore
├── CMakeLists.txt
├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
├── main.cc
├── my_application.cc
└── my_application.h
├── macos
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── app_icon_1024.png
│ │ ├── app_icon_128.png
│ │ ├── app_icon_16.png
│ │ ├── app_icon_256.png
│ │ ├── app_icon_32.png
│ │ ├── app_icon_512.png
│ │ └── app_icon_64.png
│ ├── Base.lproj
│ └── MainMenu.xib
│ ├── Configs
│ ├── AppInfo.xcconfig
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── Warnings.xcconfig
│ ├── DebugProfile.entitlements
│ ├── Info.plist
│ ├── MainFlutterWindow.swift
│ └── Release.entitlements
├── pubspec.lock
├── pubspec.yaml
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
└── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
└── app_icon.ico
├── run_loop.cpp
├── run_loop.h
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | ../lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # ObjectBox DB files
44 | **.mdb
45 |
46 | android/app/google-services.json
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 1aafb3a8b9b0c36241c5f5b34ee914770f015818
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ObjectBox Flutter Database Performance Benchmarks
2 |
3 | ## Setup
4 |
5 | As usual run `flutter pub get`.
6 |
7 | ## Running
8 |
9 | Make sure to run the app in release mode to avoid any performance penalties from debug mode.
10 | (Note: release mode is not supported on the iOS simulator, a real device is required.)
11 |
12 | To run in release mode with Android Studio connect a device or start an emulator, then
13 | Run > Flutter Run 'main.dart' in Release Mode.
14 |
15 | Or run `flutter run --release`.
16 |
17 | To further improve performance, make sure to disconnect dev tools. E.g. stop the app on the device
18 | and launch it again.
19 |
20 | ## Implementation notes
21 |
22 | ### Hive
23 |
24 | - No test with index as Hive does not support explicit indexes.
25 |
26 | ### Isar
27 |
28 | - Instead of read all does use query with no conditions and returns all results.
29 | - Query tests not available (queryWithLinks not implemented).
30 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at https://dart.dev/tools/linter-rules.
17 | #
18 | # Instead of disabling a lint rule for the entire project in the
19 | # section below, it can also be suppressed for a single line of code
20 | # or a specific dart file by using the `// ignore: name_of_lint` and
21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
22 | # producing the lint.
23 | rules:
24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26 |
27 | # Additional information about this file can be found at
28 | # https://dart.dev/tools/analysis
29 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | android {
26 | namespace "io.objectbox.dartbench"
27 | compileSdk flutter.compileSdkVersion
28 | ndkVersion flutter.ndkVersion
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | applicationId "io.objectbox.dartbench"
45 | // You can update the following values to match your application needs.
46 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
47 | minSdkVersion flutter.minSdkVersion
48 | targetSdkVersion flutter.targetSdkVersion
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 | }
52 |
53 | buildTypes {
54 | release {
55 | // TODO: Add your own signing config for the release build.
56 | // Signing with the debug keys for now, so `flutter run --release` works.
57 | signingConfig signingConfigs.debug
58 | }
59 | }
60 | }
61 |
62 | flutter {
63 | source '../..'
64 | }
65 |
66 | dependencies {}
67 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/io/objectbox/dartbench/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.objectbox.dartbench
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(':app')
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | repositories {
14 | google()
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 | }
19 |
20 | plugins {
21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22 | id "com.android.application" version "7.3.0" apply false
23 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false
24 | }
25 |
26 | include ":app"
27 |
--------------------------------------------------------------------------------
/bin/objectbox.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: avoid_print
2 |
3 | import 'dart:io';
4 |
5 | import 'package:args/args.dart';
6 | import 'package:benchapp/obx_executor.dart';
7 | import 'package:benchapp/time_tracker.dart';
8 |
9 | void main(List arguments) async {
10 | exitCode = 0; // presume success
11 |
12 | const argDb = 'db';
13 | const argCount = 'count';
14 | const argRuns = 'runs';
15 |
16 | final parser = ArgParser()
17 | ..addOption(argDb, defaultsTo: 'benchmark-db', help: 'database directory')
18 | ..addOption(argCount, defaultsTo: '10000', help: 'number of objects')
19 | ..addOption(argRuns,
20 | defaultsTo: '30', help: 'number of times the tests should be executed');
21 |
22 | final args = parser.parse(arguments);
23 | final dbDir = Directory(args[argDb]);
24 | final count = int.parse(args[argCount]);
25 | final runs = int.parse(args[argRuns]);
26 |
27 | print('running $runs times with $count objects in $dbDir');
28 |
29 | if (dbDir.existsSync()) {
30 | print('deleting existing DB directory $dbDir');
31 | dbDir.deleteSync(recursive: true);
32 | }
33 |
34 | final tracker = TimeTracker((Iterable columns) => print(columns));
35 | final bench = ExecutorPlain(dbDir, tracker);
36 |
37 | final inserts = bench.prepareData(count);
38 |
39 | for (var i = 0; i < runs; i++) {
40 | bench.insertMany(inserts);
41 | final ids = inserts.map((e) => e.id).toList(growable: false);
42 | final items = await bench.readAll(ids);
43 | bench.updateMany(items);
44 | bench.removeMany(ids);
45 |
46 | print('${i + 1}/$runs finished');
47 | }
48 |
49 | bench.close();
50 | tracker.printTimes(functions: [
51 | 'insertMany',
52 | 'readAll',
53 | 'updateMany',
54 | 'removeMany',
55 | ]);
56 | }
57 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - FMDB (2.7.5):
4 | - FMDB/standard (= 2.7.5)
5 | - FMDB/standard (2.7.5)
6 | - isar_flutter_libs (1.0.0):
7 | - Flutter
8 | - ObjectBox (1.8.1)
9 | - objectbox_flutter_libs (0.0.1):
10 | - Flutter
11 | - ObjectBox (= 1.8.1)
12 | - path_provider_foundation (0.0.1):
13 | - Flutter
14 | - FlutterMacOS
15 | - sqflite (0.0.2):
16 | - Flutter
17 | - FMDB (>= 2.7.5)
18 |
19 | DEPENDENCIES:
20 | - Flutter (from `Flutter`)
21 | - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
22 | - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
23 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
24 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
25 |
26 | SPEC REPOS:
27 | trunk:
28 | - FMDB
29 | - ObjectBox
30 |
31 | EXTERNAL SOURCES:
32 | Flutter:
33 | :path: Flutter
34 | isar_flutter_libs:
35 | :path: ".symlinks/plugins/isar_flutter_libs/ios"
36 | objectbox_flutter_libs:
37 | :path: ".symlinks/plugins/objectbox_flutter_libs/ios"
38 | path_provider_foundation:
39 | :path: ".symlinks/plugins/path_provider_foundation/ios"
40 | sqflite:
41 | :path: ".symlinks/plugins/sqflite/ios"
42 |
43 | SPEC CHECKSUMS:
44 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
45 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
46 | isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
47 | ObjectBox: a7900d5335218cd437cbc080b7ccc38a5211f7b4
48 | objectbox_flutter_libs: 61d74196d924fbc773da5f5757d1e9fab7b3cc78
49 | path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
50 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
51 |
52 | PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
53 |
54 | COCOAPODS: 1.12.0
55 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-dart-performance/9844dbb77d9b47930683821f450ffcea8c5b5725/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | benchapp
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/executor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'model.dart';
4 | import 'time_tracker.dart';
5 |
6 | class ConfigQueryWithLinks {
7 | final String sourceStringEquals;
8 | final int sourceIntEquals;
9 | final String targetStringEquals;
10 |
11 | ConfigQueryWithLinks(
12 | this.sourceStringEquals, this.sourceIntEquals, this.targetStringEquals);
13 | }
14 |
15 | abstract class ExecutorBase {
16 | static const caseSensitive = true;
17 |
18 | final TimeTracker tracker;
19 |
20 | ExecutorBase(this.tracker);
21 |
22 | Future close();
23 |
24 | /// Create an entity with the given values.
25 | T createEntity(String tString, int tInt, int tLong, double tDouble);
26 |
27 | List prepareData(int count) => List.generate(
28 | count, (i) => createEntity('Entity #$i', i, i, i.toDouble()),
29 | growable: false);
30 |
31 | void changeValues(List items) {
32 | for (var item in items) {
33 | item.tLong *= 2;
34 | }
35 | }
36 |
37 | List allNotNull(List items) =>
38 | items.map((e) => e!).toList(growable: false);
39 |
40 | /// If available should use a read all function,
41 | /// otherwise use the ID list to look up all items.
42 | Future> readAll(List optionalIds) => throw UnimplementedError();
43 |
44 | Future insertMany(List items);
45 |
46 | Future updateMany(List items);
47 |
48 | Future> queryById(List ids, [String? benchmarkQualifier]);
49 |
50 | Future removeMany(List ids);
51 |
52 | Future> queryStringEquals(List values) =>
53 | throw UnimplementedError();
54 |
55 | /// Verifies that the executor works as expected (returns proper results).
56 | Future test({required int count, String? qString}) =>
57 | Future.sync(() async {
58 | checkCount(String message, Iterable list, int count) =>
59 | RangeError.checkValueInInterval(list.length, count, count, message);
60 |
61 | final inserts = prepareData(count);
62 | await insertMany(inserts);
63 |
64 | final ids = inserts.map((e) => e.id).toList(growable: false);
65 | checkCount('insertMany assigns ids', ids.toSet(), count);
66 |
67 | final items = allNotNull(await queryById(ids));
68 | checkCount('queryById', items, count);
69 |
70 | final itemsAll = allNotNull(await readAll(ids));
71 | checkCount('readAll', itemsAll, count);
72 |
73 | changeValues(items);
74 | await updateMany(items);
75 |
76 | if (qString != null) {
77 | checkCount('query string', await queryStringEquals([qString]), 1);
78 | }
79 |
80 | checkCount('count before remove',
81 | (await queryById(ids)).where((e) => e != null), count);
82 | await removeMany(ids);
83 | checkCount('count after remove',
84 | (await queryById(ids)).where((e) => e != null), 0);
85 | });
86 |
87 | Future createRelBenchmark() => throw UnimplementedError();
88 | }
89 |
90 | /// Benchmark executor base class for relations tests
91 | abstract class ExecutorBaseRel {
92 | final TimeTracker tracker;
93 |
94 | ExecutorBaseRel(this.tracker);
95 |
96 | Future close();
97 |
98 | bool get indexed => T == RelSourceEntityIndexed;
99 |
100 | /// About 100 sources have the same group so group query condition
101 | /// returns about 100 results regardless of source object count.
102 | static int distinctSourceStrings(int sourceObjectCount) =>
103 | max(1, sourceObjectCount ~/ 100);
104 |
105 | /// Creates repeating group + target combination blocks
106 | /// where for the first distinctSourceStrings sources
107 | /// the group number and target number match.
108 | /// So overall there exist up to count / targets.length blocks
109 | /// == sources where group and target number match.
110 | /// The int is 0 or 1 based on the source index being even or odd.
111 | /// So the block length = targets.length should be odd to make sure
112 | /// every other block has a different int + group + target combination.
113 | List prepareDataSources(int count, List targets) =>
114 | List.generate(count, (i) {
115 | // Start source group number at 0 again if multiple
116 | // of target.length (= next block) reached to ensure
117 | // each block starts with matching group and target numbers.
118 | final groupNumber = i % targets.length % distinctSourceStrings(count);
119 | final string = 'Source group #$groupNumber';
120 | final target = targets[i % targets.length];
121 | // Every other source has int 0 or 1
122 | return indexed
123 | ? RelSourceEntityIndexed.forInsert(string, i % 2, target) as T
124 | : RelSourceEntityPlain.forInsert(string, i % 2, target) as T;
125 | }, growable: false);
126 |
127 | List prepareDataTargets(int count) =>
128 | List.generate(count, (i) => RelTargetEntity(0, 'Target #$i'),
129 | growable: false);
130 |
131 | Future insertData(int relSourceCount, int relTargetCount);
132 |
133 | Future> queryWithLinks(List args) =>
134 | throw UnimplementedError();
135 | }
136 |
--------------------------------------------------------------------------------
/lib/hive_executor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:hive/hive.dart';
4 |
5 | import 'executor.dart';
6 | import 'model.dart';
7 | import 'time_tracker.dart';
8 |
9 | /// Note: There are no explicit indixes in Hive.
10 | class Executor extends ExecutorBase {
11 | final Box _box;
12 | final String _dir;
13 |
14 | Executor._(this._dir, this._box, TimeTracker tracker) : super(tracker);
15 |
16 | static Future create(Directory dbDir, TimeTracker tracker) async {
17 | if (!Hive.isAdapterRegistered(TestEntityPlainAdapter().typeId)) {
18 | Hive.registerAdapter(TestEntityPlainAdapter());
19 | }
20 | return Executor._(
21 | dbDir.path,
22 | await Hive.openBox((TestEntityPlain).toString(), path: dbDir.path),
23 | tracker);
24 | }
25 |
26 | @override
27 | Future close() async => await _box.close();
28 |
29 | @override
30 | TestEntityPlain createEntity(
31 | String tString, int tInt, int tLong, double tDouble) {
32 | return TestEntityPlain(0, tString, tInt, tLong, tDouble);
33 | }
34 |
35 | @override
36 | Future insertMany(List items) async =>
37 | tracker.trackAsync('insertMany', () async {
38 | int id = 1;
39 | final itemsById = {};
40 | for (var o in items) {
41 | if (o.id == 0) o.id = id++;
42 | itemsById[o.id] = o;
43 | }
44 | return await _box.putAll(itemsById);
45 | });
46 |
47 | @override
48 | Future updateMany(List items) async =>
49 | tracker.trackAsync('updateMany',
50 | () async => await _box.putAll({for (var o in items) o.id: o}));
51 |
52 | @override
53 | Future> readAll(List optionalIds) =>
54 | Future.value(tracker.track('readAll', () => _box.values.toList()));
55 |
56 | @override
57 | Future> queryById(List ids,
58 | [String? benchmarkQualifier]) =>
59 | Future.value(tracker.track('queryById${benchmarkQualifier ?? ''}',
60 | () => ids.map(_box.get).toList()));
61 |
62 | @override
63 | Future removeMany(List ids) async =>
64 | tracker.trackAsync('removeMany', () async {
65 | await _box.deleteAll(ids);
66 | await _box.compact();
67 | });
68 |
69 | // Hive doesn't have queries -> let's emulate
70 | @override
71 | Future> queryStringEquals(List values) {
72 | if (!ExecutorBase.caseSensitive) {
73 | values = values.map((e) => e.toLowerCase()).toList(growable: false);
74 | }
75 | return Future.value(tracker.track('queryStringEquals', () {
76 | late List result;
77 | final length = values.length;
78 | for (var i = 0; i < length; i++) {
79 | result = _box.values
80 | .where((TestEntityPlain object) => ExecutorBase.caseSensitive
81 | ? object.tString == values[i]
82 | : object.tString.toLowerCase() == values[i])
83 | .toList();
84 | }
85 | return result;
86 | }));
87 | }
88 |
89 | @override
90 | Future createRelBenchmark() =>
91 | ExecutorRel.create(tracker, _dir);
92 | }
93 |
94 | class ExecutorRel extends ExecutorBaseRel {
95 | final Box _box;
96 | final Box _boxTarget;
97 |
98 | static Future> create(
99 | TimeTracker tracker, String? path) async {
100 | if (!Hive.isAdapterRegistered(RelSourceEntityPlainAdapter().typeId)) {
101 | Hive.registerAdapter(RelSourceEntityPlainAdapter());
102 | }
103 | if (!Hive.isAdapterRegistered(RelTargetEntityAdapter().typeId)) {
104 | Hive.registerAdapter(RelTargetEntityAdapter());
105 | }
106 | return ExecutorRel._(tracker, await Hive.openBox(T.toString(), path: path),
107 | await Hive.openBox('RelTargetEntity', path: path));
108 | }
109 |
110 | ExecutorRel._(super.tracker, this._box, this._boxTarget);
111 |
112 | @override
113 | Future close() async {
114 | await _box.close();
115 | await _boxTarget.close();
116 | }
117 |
118 | @override
119 | Future insertData(int relSourceCount, int relTargetCount) async {
120 | final targets = prepareDataTargets(relTargetCount);
121 | await _boxTarget.putAll(_itemsById(targets));
122 | assert(targets.first.id != 0);
123 | final sources = prepareDataSources(relSourceCount, targets);
124 | await _box.putAll(_itemsById(sources));
125 | assert(_box.length == relSourceCount);
126 | assert(_boxTarget.length == relTargetCount);
127 | }
128 |
129 | @override
130 | Future> queryWithLinks(List args) {
131 | if (!ExecutorBase.caseSensitive) {
132 | for (var config in args) {
133 | config.sourceStringEquals.toLowerCase();
134 | config.targetStringEquals.toLowerCase();
135 | }
136 | }
137 |
138 | return Future.value(tracker.track('queryWithLinks', () {
139 | late List result;
140 | final length = args.length;
141 | for (var i = 0; i < length; i++) {
142 | final matchingTargets = _boxTarget.values
143 | .where((RelTargetEntity o) =>
144 | (ExecutorBase.caseSensitive ? o.name : o.name.toLowerCase()) ==
145 | args[i].targetStringEquals)
146 | .map((e) => e.id)
147 | .toSet();
148 | assert(matchingTargets.isNotEmpty);
149 | result = _box.values
150 | .where((T o) =>
151 | o.tLong == args[i].sourceIntEquals &&
152 | matchingTargets
153 | .contains((o as RelSourceEntityPlain).relTargetId) &&
154 | (ExecutorBase.caseSensitive
155 | ? o.tString
156 | : o.tString.toLowerCase()) ==
157 | args[i].sourceStringEquals)
158 | .toList();
159 | }
160 | return result;
161 | }));
162 | }
163 | }
164 |
165 | Map _itemsById(List list) {
166 | final result = {};
167 | var id = 1;
168 | for (var o in list) {
169 | if (o.id == 0) o.id = id++;
170 | result[o.id] = o as EntityT;
171 | }
172 | return result;
173 | }
174 |
--------------------------------------------------------------------------------
/lib/isar_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:benchapp/model.dart';
2 | import 'package:isar/isar.dart';
3 |
4 | part 'isar_model.g.dart';
5 |
6 | @collection
7 | class IsarTestEntityPlain implements TestEntity {
8 | @override
9 | Id id;
10 |
11 | @override
12 | String tString;
13 |
14 | @override
15 | int tInt; // 32-bit
16 |
17 | @override
18 | int tLong; // 64-bit
19 |
20 | @override
21 | double tDouble;
22 |
23 | IsarTestEntityPlain(
24 | this.id, this.tString, this.tInt, this.tLong, this.tDouble);
25 | }
26 |
27 | // A separate entity for queried data so that indexes don't change CRUD results.
28 | @Collection()
29 | class IsarTestEntityIndexed implements TestEntity {
30 | @override
31 | Id id;
32 |
33 | @override
34 | @Index(type: IndexType.value)
35 | String tString;
36 |
37 | @override
38 | @Index()
39 | int tInt; // 32-bit
40 |
41 | @override
42 | int tLong; // 64-bit
43 |
44 | @override
45 | double tDouble;
46 |
47 | IsarTestEntityIndexed(
48 | this.id, this.tString, this.tInt, this.tLong, this.tDouble);
49 | }
50 |
51 | @collection
52 | class IsarRelSourceEntityPlain implements RelSourceEntity {
53 | @override
54 | Id id;
55 |
56 | @override
57 | final String tString;
58 |
59 | @override
60 | final int tLong; // 64-bit
61 |
62 | @override
63 | @Ignore()
64 | final int relTargetId;
65 |
66 | final isarRelTarget = IsarLink();
67 |
68 | // Note: constructor arg types must match with fromMap used by sqflite.
69 | IsarRelSourceEntityPlain(this.id, this.tString, this.tLong,
70 | [this.relTargetId = 0]);
71 |
72 | IsarRelSourceEntityPlain.forInsert(
73 | this.tString, this.tLong, IsarRelTargetEntity? relTarget)
74 | : id = 0,
75 | relTargetId = relTarget?.id ?? 0 {
76 | isarRelTarget.value = relTarget;
77 | }
78 | }
79 |
80 | @collection
81 | class IsarRelSourceEntityIndexed implements RelSourceEntity {
82 | @override
83 | Id id;
84 |
85 | @override
86 | @Index(type: IndexType.value)
87 | final String tString;
88 |
89 | @override
90 | final int tLong; // 64-bit
91 |
92 | @override
93 | @Ignore()
94 | final int relTargetId;
95 |
96 | final isarRelTarget = IsarLink();
97 |
98 | // Note: constructor arg types must match with fromMap used by sqflite.
99 | IsarRelSourceEntityIndexed(this.id, this.tString, this.tLong,
100 | [this.relTargetId = 0]);
101 |
102 | IsarRelSourceEntityIndexed.forInsert(
103 | this.tString, this.tLong, IsarRelTargetEntity? relTarget)
104 | : id = 0,
105 | relTargetId = relTarget?.id ?? 0 {
106 | isarRelTarget.value = relTarget;
107 | }
108 | }
109 |
110 | @collection
111 | class IsarRelTargetEntity extends EntityWithSettableId {
112 | @override
113 | Id id;
114 |
115 | @Index(type: IndexType.value)
116 | String name;
117 |
118 | IsarRelTargetEntity(this.id, this.name);
119 | }
120 |
--------------------------------------------------------------------------------
/lib/isar_sync_executor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:benchapp/isar_model.dart';
4 | import 'package:isar/isar.dart';
5 |
6 | import 'executor.dart';
7 | import 'model.dart';
8 | import 'time_tracker.dart';
9 |
10 | abstract class Executor extends ExecutorBase {
11 | final Isar _store;
12 | final IsarCollection _box;
13 |
14 | Executor._(this._store, TimeTracker tracker)
15 | : _box = _store.collection(),
16 | super(tracker);
17 |
18 | @override
19 | Future close() async => await _store.close();
20 |
21 | @override
22 | Future insertMany(List items) => Future.value(
23 | tracker.track(
24 | 'insertMany',
25 | () {
26 | _assignIds(items);
27 | return _store.writeTxnSync(
28 | () => _box.putAllSync(items),
29 | );
30 | },
31 | ),
32 | );
33 |
34 | @override
35 | Future updateMany(List items) => Future.value(tracker.track(
36 | 'updateMany', () => _store.writeTxnSync(() => _box.putAllSync(items))));
37 |
38 | // Note: get all is not supported in isar (v2.5.0), instead
39 | // use query with no conditions as suggested in Quickstart.
40 | @override
41 | Future> readAll(List optionalIds) =>
42 | Future.value(tracker.track('readAll', () => _box.where().findAllSync()));
43 |
44 | @override
45 | Future> queryById(List ids, [String? benchmarkQualifier]) =>
46 | Future.value(tracker.track(
47 | 'queryById${benchmarkQualifier ?? ''}', () => _box.getAllSync(ids)));
48 |
49 | @override
50 | Future removeMany(List ids) => Future.value(
51 | tracker.track('removeMany',
52 | () => _store.writeTxnSync(() => _box.deleteAllSync(ids))),
53 | );
54 | }
55 |
56 | /// Using an entity without indexes
57 | class ExecutorPlain extends Executor {
58 | ExecutorPlain._(super.store, super.tracker) : super._();
59 |
60 | static Future> create(
61 | Directory dbDir, TimeTracker tracker) async {
62 | final isar = await Isar.open(
63 | [
64 | IsarTestEntityPlainSchema,
65 | IsarRelSourceEntityPlainSchema,
66 | IsarRelTargetEntitySchema
67 | ],
68 | directory: dbDir.path,
69 | );
70 | return ExecutorPlain._(isar, tracker);
71 | }
72 |
73 | @override
74 | IsarTestEntityPlain createEntity(
75 | String tString, int tInt, int tLong, double tDouble) {
76 | return IsarTestEntityPlain(0, tString, tInt, tLong, tDouble);
77 | }
78 |
79 | @override
80 | Future> queryStringEquals(
81 | List values) async =>
82 | Future.value(tracker.track('queryStringEquals', () {
83 | late List result;
84 | final length = values.length;
85 | for (var i = 0; i < length; i++) {
86 | result = (_box)
87 | .where()
88 | .filter()
89 | .tStringEqualTo(values[i],
90 | caseSensitive: ExecutorBase.caseSensitive)
91 | .findAllSync();
92 | }
93 | return result;
94 | }));
95 |
96 | @override
97 | Future createRelBenchmark() =>
98 | Future.value(ExecutorRel._(tracker, _store));
99 | }
100 |
101 | /// Using an entity with indexes
102 | class ExecutorIndexed extends Executor {
103 | ExecutorIndexed._(super.store, super.tracker) : super._();
104 |
105 | static Future> create(
106 | Directory dbDir, TimeTracker tracker) async {
107 | final isar = await Isar.open(
108 | [
109 | IsarTestEntityIndexedSchema,
110 | IsarRelSourceEntityIndexedSchema,
111 | IsarRelTargetEntitySchema
112 | ],
113 | directory: dbDir.path,
114 | );
115 | return ExecutorIndexed._(isar, tracker);
116 | }
117 |
118 | @override
119 | IsarTestEntityIndexed createEntity(
120 | String tString, int tInt, int tLong, double tDouble) {
121 | return IsarTestEntityIndexed(0, tString, tInt, tLong, tDouble);
122 | }
123 |
124 | @override
125 | Future> queryStringEquals(
126 | List values) async =>
127 | Future.value(tracker.track('queryStringEquals', () {
128 | // Indexed queries must be case insensitive, this prevents comparison.
129 | // See https://github.com/isar/isar#queries
130 | assert(!ExecutorBase.caseSensitive);
131 | late List result;
132 | final length = values.length;
133 | for (var i = 0; i < length; i++) {
134 | result = (_box).where().tStringEqualTo(values[i]).findAllSync();
135 | }
136 | return result;
137 | }));
138 |
139 | @override
140 | Future createRelBenchmark() =>
141 | Future.value(ExecutorRel._(tracker, _store));
142 | }
143 |
144 | class ExecutorRel extends ExecutorBaseRel {
145 | final Isar _store;
146 | final IsarCollection _box;
147 | final IsarCollection _boxTarget;
148 |
149 | ExecutorRel._(super.tracker, this._store)
150 | : _box = _store.collection(),
151 | _boxTarget = _store.isarRelTargetEntitys;
152 |
153 | @override
154 | Future close() async => Future.value();
155 |
156 | @override
157 | Future insertData(int relSourceCount, int relTargetCount) async {
158 | final targets = prepareDataTargetsIsar(relTargetCount);
159 | _assignIds(targets);
160 | _store.writeTxnSync(() => _boxTarget.putAllSync(targets));
161 |
162 | final sources = prepareDataSourcesIsar(relSourceCount, targets);
163 | _assignIds(sources);
164 | _store.writeTxnSync(() => _box.putAllSync(sources));
165 |
166 | assert(_box.countSync() == relSourceCount);
167 | assert(_boxTarget.countSync() == relTargetCount);
168 | }
169 |
170 | /// Matches ExecutorBaseRel.prepareDataSources, see for implementation details.
171 | List prepareDataSourcesIsar(
172 | int count, List targets) =>
173 | List.generate(count, (i) {
174 | final groupNumber =
175 | i % targets.length % ExecutorBaseRel.distinctSourceStrings(count);
176 | final string = 'Source group #$groupNumber';
177 | final target = targets[i % targets.length];
178 | return indexed
179 | ? IsarRelSourceEntityIndexed.forInsert(string, i % 2, target) as T
180 | : IsarRelSourceEntityPlain.forInsert(string, i % 2, target) as T;
181 | }, growable: false);
182 |
183 | List prepareDataTargetsIsar(int count) =>
184 | List.generate(count, (i) => IsarRelTargetEntity(0, 'Target #$i'),
185 | growable: false);
186 |
187 | @override
188 | Future> queryWithLinks(List args) {
189 | // TODO implement once the model is properly generated
190 | // see https://isar.dev/queries#links
191 | return Future.error(UnimplementedError('queryWithLinks'));
192 | }
193 | }
194 |
195 | void _assignIds(List list) {
196 | for (var i = 0; i < list.length; i++) {
197 | list[i].id = i + 1;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 | import 'dart:math';
4 |
5 | import 'package:flutter/material.dart';
6 | import 'package:path/path.dart' as path;
7 | import 'package:path_provider/path_provider.dart';
8 |
9 | import 'executor.dart';
10 | import 'hive_executor.dart' as hive;
11 | import 'isar_sync_executor.dart' as isar_sync;
12 | import 'obx_executor.dart' as obx;
13 | import 'sqf_executor.dart' as sqf;
14 |
15 | // import 'hive_lazy_executor.dart' as hive_lazy;
16 | import 'time_tracker.dart';
17 |
18 | void main() {
19 | runApp(const MyApp());
20 | }
21 |
22 | class MyApp extends StatelessWidget {
23 | const MyApp({super.key});
24 |
25 | // This widget is the root of your application.
26 | @override
27 | Widget build(BuildContext context) {
28 | return MaterialApp(
29 | title: 'DB Benchmark',
30 | theme: ThemeData(
31 | // This is the theme of your application.
32 | //
33 | // Try running your application with "flutter run". You'll see the
34 | // application has a blue toolbar. Then, without quitting the app, try
35 | // changing the primarySwatch below to Colors.green and then invoke
36 | // "hot reload" (press "r" in the console where you ran "flutter run",
37 | // or simply save your changes to "hot reload" in a Flutter IDE).
38 | // Notice that the counter didn't reset back to zero; the application
39 | // is not restarted.
40 | primarySwatch: Colors.blue,
41 | // This makes the visual density adapt to the platform that you run
42 | // the app on. For desktop platforms, the controls will be smaller and
43 | // closer together (more dense) than on mobile platforms.
44 | visualDensity: VisualDensity.adaptivePlatformDensity,
45 | ),
46 | home: const MyHomePage(title: 'DB Benchmark'),
47 | );
48 | }
49 | }
50 |
51 | class MyHomePage extends StatefulWidget {
52 | const MyHomePage({super.key, required this.title});
53 |
54 | // This widget is the home page of your application. It is stateful, meaning
55 | // that it has a State object (defined below) that contains fields that affect
56 | // how it looks.
57 |
58 | // This class is the configuration for the state. It holds the values (in this
59 | // case the title) provided by the parent (in this case the App widget) and
60 | // used by the build method of the State. Fields in a Widget subclass are
61 | // always marked "final".
62 |
63 | final String title;
64 |
65 | @override
66 | State createState() => _MyHomePageState();
67 | }
68 |
69 | enum DbEngine { ObjectBox, sqflite, Hive, IsarSync }
70 |
71 | enum Mode { CRUD, Queries, QueryById }
72 |
73 | enum RunState { idle, running, stopping }
74 |
75 | class _MyHomePageState extends State {
76 | var _db = DbEngine.ObjectBox;
77 | var _mode = Mode.CRUD;
78 | var _indexed = false;
79 | final _objectsController = TextEditingController(text: '10000');
80 | final _runsController = TextEditingController(text: '10');
81 | final _operationsController = TextEditingController(text: '1000');
82 | final _resultsController = TextEditingController(text: '10000');
83 | late final TimeTracker _tracker = TimeTracker(_print);
84 |
85 | /// The directory where a temporary directory for each benchmark is created in.
86 | final appDir = Completer();
87 |
88 | var _result = '';
89 | final _resultRows = [];
90 | RunState _state = RunState.idle;
91 |
92 | void _print(List columns) {
93 | setState(() {
94 | final cols = [];
95 | for (var i = 0; i < columns.length; i++) {
96 | cols.add(Text(columns[i],
97 | softWrap: false,
98 | style: TextStyle(
99 | fontSize: 20,
100 | fontWeight: (_resultRows.isEmpty || columns[i] == 'Count')
101 | ? FontWeight.bold
102 | : FontWeight.normal),
103 | textAlign: i == 0 ? TextAlign.left : TextAlign.right));
104 | }
105 | _resultRows.add(TableRow(children: cols));
106 | });
107 | }
108 |
109 | void configure(DbEngine db, Mode mode, bool indexed) => setState(() {
110 | _db = db;
111 | _mode = mode;
112 | _indexed = indexed;
113 | _result = '';
114 | _resultRows.clear();
115 | });
116 |
117 | @override
118 | void initState() {
119 | super.initState();
120 |
121 | /// Use getTemporaryDirectory instead of getApplicationDocumentsDirectory to
122 | /// avoid creating and deleting many files in a potentially backed up
123 | /// location on the test device.
124 | getTemporaryDirectory().then((value) {
125 | debugPrint("Using directory: ${value.absolute.path}");
126 | appDir.complete(value);
127 | });
128 | }
129 |
130 | Future _createExecutor(Directory dbDir, bool useIndexes) async {
131 | switch (_db) {
132 | case DbEngine.ObjectBox:
133 | if (useIndexes) {
134 | return Future.value(obx.ExecutorIndexed(dbDir, _tracker));
135 | } else {
136 | return Future.value(obx.ExecutorPlain(dbDir, _tracker));
137 | }
138 | case DbEngine.sqflite:
139 | final dbFile = Directory(path.join(dbDir.path, 'bench.db'));
140 | if (useIndexes) {
141 | return sqf.ExecutorIndexed.create(dbFile, _tracker);
142 | } else {
143 | return sqf.ExecutorPlain.create(dbFile, _tracker);
144 | }
145 | case DbEngine.Hive:
146 | // No explicit index support for Hive.
147 | return hive.Executor.create(dbDir, _tracker);
148 | case DbEngine.IsarSync:
149 | if (useIndexes) {
150 | return isar_sync.ExecutorIndexed.create(dbDir, _tracker);
151 | } else {
152 | return isar_sync.ExecutorPlain.create(dbDir, _tracker);
153 | }
154 | default:
155 | throw Exception('Unknown executor');
156 | }
157 | }
158 |
159 | bool get indexed => _indexed && _db != DbEngine.Hive;
160 |
161 | void _stopBenchmark() async => setState(() {
162 | _result = 'Benchmark stopping...';
163 | _state = RunState.stopping;
164 | });
165 |
166 | void _runBenchmark() async {
167 | setState(() {
168 | _result = 'Benchmark starting...';
169 | _resultRows.clear();
170 | _state = RunState.running;
171 | });
172 |
173 | final dbDir = (await appDir.future).createTempSync();
174 | print('Using temporary DB directory $dbDir');
175 | dbDir.createSync(recursive: true);
176 |
177 | ExecutorBase? executor;
178 | try {
179 | executor = await _createExecutor(dbDir, indexed);
180 | await _runBenchmarkOn(executor);
181 | } finally {
182 | await executor?.close();
183 | if (dbDir.existsSync()) dbDir.deleteSync(recursive: true);
184 | }
185 | }
186 |
187 | Future printResult(String value) async {
188 | setState(() => _result = value);
189 | await Future.delayed(const Duration(seconds: 0)); // yield to re-render
190 | }
191 |
192 | /// Waits for the given future to complete. Returns true if the benchmark
193 | /// should continue or false if it was stopped by the user in the meantime.
194 | Future awaitOrStop(Future future) async {
195 | await Future.any([
196 | future,
197 | Future.microtask(() async {
198 | while (_state == RunState.running) {
199 | await Future.delayed(const Duration(milliseconds: 10));
200 | }
201 | return Future.value();
202 | })
203 | ]);
204 | return Future.value(_state == RunState.running);
205 | }
206 |
207 | Future _runBenchmarkOn(ExecutorBase bench) async {
208 | final objectsCount = int.parse(_objectsController.value.text);
209 | final runs = int.parse(_runsController.value.text);
210 |
211 | // Before we start to benchmark: verify the executor works as expected.
212 | // assert() makes this only run in debug mode
213 | assert(await _testBenchmark(bench));
214 |
215 | _tracker.clear();
216 |
217 | try {
218 | switch (_mode) {
219 | case Mode.CRUD:
220 | for (var i = 0; i < runs && _state == RunState.running; i++) {
221 | final inserts = bench.prepareData(objectsCount);
222 | if (!await awaitOrStop(bench.insertMany(inserts))) {
223 | break;
224 | }
225 | final ids = inserts.map((e) => e.id).toList(growable: false);
226 | final itemsOptional = await bench.readAll(ids);
227 | final items = bench.allNotNull(itemsOptional);
228 | assert(items.length == objectsCount);
229 | bench.changeValues(items);
230 | if (!await awaitOrStop(bench.updateMany(items))) {
231 | break;
232 | }
233 | await bench.removeMany(ids);
234 |
235 | await printResult('$_mode: ${i + 1}/$runs finished');
236 | }
237 |
238 | if (_state == RunState.running) {
239 | _tracker.printTimes(avgOnly: true, functions: [
240 | 'insertMany',
241 | 'readAll',
242 | 'updateMany',
243 | 'removeMany',
244 | ]);
245 | }
246 | break;
247 |
248 | case Mode.Queries:
249 | final random = Random();
250 | final operationsCount = int.parse(_operationsController.value.text);
251 | await printResult('Preparing data...');
252 | final inserts = bench.prepareData(objectsCount);
253 | if (!await awaitOrStop(bench.insertMany(inserts))) {
254 | break;
255 | }
256 |
257 | final relBench = await bench.createRelBenchmark();
258 | // About 9 sources have the same target
259 | // Ensure target count is uneven to not align with odd/even int values.
260 | final targetCount = objectsCount ~/ 10;
261 | final relTargetsCount =
262 | max(1, targetCount.isEven ? targetCount - 1 : targetCount);
263 | await relBench.insertData(objectsCount, relTargetsCount);
264 | final distinctSourceStrings =
265 | ExecutorBaseRel.distinctSourceStrings(objectsCount);
266 | debugPrint(
267 | "source groups = $distinctSourceStrings, targets = $relTargetsCount");
268 |
269 | final resultCounts = List.filled(3, -1);
270 |
271 | for (var i = 0; i < runs && _state == RunState.running; i++) {
272 | final qStringValues = List.generate(operationsCount,
273 | (_) => inserts[random.nextInt(objectsCount)].tString,
274 | growable: false);
275 | final qStringMatching =
276 | await bench.queryStringEquals(qStringValues);
277 | assert(qStringMatching.length == 1);
278 |
279 | final qLinkConfigs = List.generate(operationsCount, (_) {
280 | // Ensures 5-6 results (depending on how many int condition filters).
281 | // Also see prepareDataSources function in executor.
282 | final number = random.nextInt(distinctSourceStrings);
283 | return ConfigQueryWithLinks('Source group #$number',
284 | random.nextInt(2), 'Target #$number');
285 | }, growable: false);
286 | final relResults = await relBench.queryWithLinks(qLinkConfigs);
287 | RangeError.checkValueInInterval(
288 | relResults.length, 5, 6, 'queryWithLinks results length');
289 |
290 | await printResult('$_mode: ${i + 1}/$runs finished');
291 |
292 | resultCounts[0] = qStringMatching.length;
293 | resultCounts[1] = relResults.length;
294 | }
295 |
296 | if (_state == RunState.running) {
297 | _tracker.printTimes(avgOnly: true, functions: [
298 | 'queryStringEquals',
299 | 'queryWithLinks',
300 | ]);
301 |
302 | _print(['', '']);
303 | _print(['', 'Count']);
304 | _print(['queryStringEquals', resultCounts[0].toString()]);
305 | _print(['queryWithLinks', resultCounts[1].toString()]);
306 |
307 | // just so that the test after benchmarks passes
308 | await bench.removeMany(inserts.map((e) => e.id).toList());
309 | }
310 |
311 | await relBench.close();
312 |
313 | break;
314 | case Mode.QueryById:
315 | final random = Random();
316 | final resultsCount = int.parse(_resultsController.value.text);
317 |
318 | await printResult('Preparing data...');
319 | final inserts = bench.prepareData(objectsCount);
320 | if (!await awaitOrStop(bench.insertMany(inserts))) {
321 | break;
322 | }
323 |
324 | final ids = inserts.map((e) => e.id).toList(growable: false);
325 |
326 | randomSlice(List list, int length) {
327 | final start = list.length == length
328 | ? 0
329 | : random.nextInt(list.length - length);
330 | final result = list.sublist(start, start + length);
331 | assert(result.length == length);
332 | return result;
333 | }
334 |
335 | int resultCount = 0;
336 | for (var i = 0; i < runs && _state == RunState.running; i++) {
337 | final idsShuffled = (ids.toList(growable: false))..shuffle(random);
338 | final randomSliceLength = min(ids.length, resultsCount);
339 | final qByIdItems = await bench.queryById(
340 | randomSlice(idsShuffled, randomSliceLength), '(random)');
341 | final qByIdItems2 =
342 | await bench.queryById(randomSlice(ids, randomSliceLength));
343 | assert(qByIdItems.length == qByIdItems2.length);
344 |
345 | await printResult('$_mode: ${i + 1}/$runs finished');
346 |
347 | resultCount = qByIdItems.length;
348 | }
349 |
350 | if (_state == RunState.running) {
351 | _tracker.printTimes(avgOnly: true, functions: [
352 | 'queryById',
353 | 'queryById(random)',
354 | ]);
355 |
356 | _print(['', '']);
357 | _print(['', 'Count']);
358 | _print(['queryById', resultCount.toString()]);
359 |
360 | // just so that the test after benchmarks passes
361 | await bench.removeMany(inserts.map((e) => e.id).toList());
362 | }
363 |
364 | break;
365 | }
366 | } catch (e) {
367 | setState(() {
368 | _result = "Benchmark failed: $e";
369 | _state = RunState.idle;
370 | });
371 | rethrow;
372 | }
373 |
374 | // Sanity check after the benchmark: subsequent runs must have same results.
375 | assert(await _testBenchmark(bench));
376 |
377 | if (_state == RunState.stopping) {
378 | setState(() {
379 | _result = 'Benchmark stopped';
380 | _resultRows.clear();
381 | });
382 | }
383 |
384 | setState(() {
385 | _state = RunState.idle;
386 | });
387 | }
388 |
389 | Future _testBenchmark(ExecutorBase bench) async {
390 | try {
391 | const count = 100;
392 | await bench.test(
393 | count: count,
394 | qString: _mode == Mode.Queries
395 | ? bench.prepareData((count / 2).floor()).last.tString
396 | : null);
397 | } catch (e) {
398 | setState(() {
399 | _result = "Executor test failed: $e";
400 | });
401 | }
402 | return true;
403 | }
404 |
405 | @override
406 | Widget build(BuildContext context) {
407 | // This method is rerun every time setState is called, for instance as done
408 | // by the _incrementCounter method above.
409 | //
410 | // The Flutter framework has been optimized to make rerunning build methods
411 | // fast, so that you can just rebuild anything that needs updating rather
412 | // than having to individually change instances of widgets.
413 | return Scaffold(
414 | appBar: AppBar(
415 | // Here we take the value from the MyHomePage object that was created by
416 | // the App.build method, and use it to set our appbar title.
417 | title: Text(widget.title),
418 | ),
419 | body: Center(
420 | // Center is a layout widget. It takes a single child and positions it
421 | // in the middle of the parent.
422 | child: Column(
423 | mainAxisAlignment: MainAxisAlignment.center,
424 | children: [
425 | Row(children: [
426 | const Spacer(),
427 | DropdownButton(
428 | value: _db,
429 | items: enumDropDownItems(DbEngine.values),
430 | onChanged: (DbEngine? value) =>
431 | configure(value!, _mode, _indexed)),
432 | const Spacer(),
433 | DropdownButton(
434 | value: _mode,
435 | // TODO items: enumDropDownItems(Mode.values),
436 | // Isar queries can't be evaluated yet because the model
437 | // doesn't work relations.
438 | // Note: evaluating just stringEquals() isn't an option
439 | // because it would be heavily optimized by the VM if
440 | // it's the only function executed and wouldn't be
441 | // comparable to other databases that execute other
442 | // benchmarks in the same loop.
443 | items: enumDropDownItems(Mode.values
444 | .where((mode) =>
445 | _db != DbEngine.IsarSync ||
446 | (mode != Mode.Queries && mode != Mode.QueryById))
447 | .toList()),
448 | onChanged: (Mode? value) => configure(_db, value!, _indexed)),
449 | const Spacer(),
450 | const Text('Index'),
451 | if (_db == DbEngine.Hive)
452 | const Text(' not available')
453 | else
454 | Switch(
455 | value: _indexed,
456 | onChanged: (bool value) => configure(_db, _mode, value),
457 | activeTrackColor: Colors.yellow,
458 | activeColor: Colors.orangeAccent,
459 | ),
460 | const Spacer(),
461 | ]),
462 | Row(children: [
463 | const Spacer(),
464 | Expanded(
465 | child: TextField(
466 | keyboardType: TextInputType.number,
467 | controller: _runsController,
468 | decoration: const InputDecoration(
469 | labelText: 'Runs',
470 | ),
471 | )),
472 | if (_mode == Mode.Queries) const Spacer(),
473 | if (_mode == Mode.Queries)
474 | Expanded(
475 | child: TextField(
476 | keyboardType: TextInputType.number,
477 | controller: _operationsController,
478 | decoration: const InputDecoration(
479 | labelText: 'Operations',
480 | ),
481 | )),
482 | if (_mode == Mode.QueryById) const Spacer(),
483 | if (_mode == Mode.QueryById)
484 | Expanded(
485 | child: TextField(
486 | keyboardType: TextInputType.number,
487 | controller: _resultsController,
488 | decoration: const InputDecoration(
489 | labelText: 'Results',
490 | ),
491 | )),
492 | const Spacer(),
493 | Expanded(
494 | child: TextField(
495 | keyboardType: TextInputType.number,
496 | controller: _objectsController,
497 | decoration: const InputDecoration(
498 | labelText: 'Objects',
499 | ),
500 | )),
501 | const Spacer(),
502 | ]),
503 | const Spacer(),
504 | Text(_result),
505 | const Spacer(),
506 | Container(
507 | padding: const EdgeInsets.symmetric(horizontal: 30),
508 | child: Table(
509 | border: const TableBorder(
510 | horizontalInside: BorderSide(color: Color(0x55000000))),
511 | children: _resultRows)),
512 | const Spacer(),
513 | ],
514 | ),
515 | ),
516 | floatingActionButton: _state == RunState.stopping
517 | ? null
518 | : _state == RunState.running
519 | ? FloatingActionButton(
520 | onPressed: _stopBenchmark,
521 | tooltip: 'Stop',
522 | child: const Icon(Icons.stop),
523 | )
524 | : FloatingActionButton(
525 | onPressed: _runBenchmark,
526 | tooltip: 'Start',
527 | child: const Icon(Icons.play_arrow),
528 | ), // This trailing comma makes auto-formatting nicer for build methods.
529 | );
530 | }
531 | }
532 |
533 | List> enumDropDownItems(List values) => values
534 | .map((dynamic e) => DropdownMenuItem(
535 | value: e,
536 | child:
537 | Text(e.toString().substring(e.runtimeType.toString().length + 1)),
538 | ))
539 | .toList();
540 |
--------------------------------------------------------------------------------
/lib/model.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 | import 'package:objectbox/objectbox.dart' as obx;
3 |
4 | part 'model.g.dart';
5 |
6 | abstract class EntityWithSettableId {
7 | int get id;
8 |
9 | set id(int value);
10 | }
11 |
12 | abstract class TestEntity extends EntityWithSettableId {
13 | String get tString;
14 |
15 | int get tInt; // 32-bit
16 |
17 | int get tLong; // 64-bit
18 | set tLong(int value);
19 |
20 | double get tDouble;
21 |
22 | static Map toMap(TestEntity object) => {
23 | 'id': object.id == 0 ? null : object.id,
24 | 'tString': object.tString,
25 | 'tInt': object.tInt,
26 | 'tLong': object.tLong,
27 | 'tDouble': object.tDouble
28 | };
29 | }
30 |
31 | @obx.Entity()
32 | @HiveType(typeId: 1)
33 | class TestEntityPlain implements TestEntity {
34 | @override
35 | @HiveField(0)
36 | int id;
37 |
38 | @override
39 | @HiveField(1)
40 | String tString;
41 |
42 | @override
43 | @obx.Property(type: obx.PropertyType.int)
44 | @HiveField(2)
45 | int tInt; // 32-bit
46 |
47 | @override
48 | @HiveField(3)
49 | int tLong; // 64-bit
50 |
51 | @override
52 | @HiveField(4)
53 | double tDouble;
54 |
55 | TestEntityPlain(this.id, this.tString, this.tInt, this.tLong, this.tDouble);
56 |
57 | static TestEntityPlain fromMap(Map map) => TestEntityPlain(
58 | map['id'] ?? 0,
59 | map['tString'],
60 | map['tInt'],
61 | map['tLong'],
62 | map['tDouble']);
63 | }
64 |
65 | // A separate entity for queried data so that indexes don't change CRUD results.
66 | @obx.Entity()
67 | class TestEntityIndexed implements TestEntity {
68 | @override
69 | int id;
70 |
71 | @override
72 | @obx.Index()
73 | String tString;
74 |
75 | @override
76 | @obx.Index()
77 | @obx.Property(type: obx.PropertyType.int)
78 | int tInt; // 32-bit
79 |
80 | @override
81 | int tLong; // 64-bit
82 |
83 | @override
84 | double tDouble;
85 |
86 | TestEntityIndexed(this.id, this.tString, this.tInt, this.tLong, this.tDouble);
87 |
88 | static TestEntityIndexed fromMap(Map map) =>
89 | TestEntityIndexed(map['id'] ?? 0, map['tString'], map['tInt'],
90 | map['tLong'], map['tDouble']);
91 | }
92 |
93 | abstract class RelSourceEntity extends EntityWithSettableId {
94 | @override
95 | int get id;
96 |
97 | @override
98 | set id(int value);
99 |
100 | String get tString;
101 |
102 | int get tLong;
103 |
104 | // for hive & sqflite
105 | int get relTargetId;
106 |
107 | static Map toMap(RelSourceEntity object) =>
108 | {
109 | 'id': object.id == 0 ? null : object.id,
110 | 'tString': object.tString,
111 | 'tLong': object.tLong,
112 | 'relTargetId': object.relTargetId,
113 | };
114 | }
115 |
116 | @obx.Entity()
117 | @HiveType(typeId: 2)
118 | class RelSourceEntityPlain implements RelSourceEntity {
119 | @override
120 | @HiveField(0)
121 | int id;
122 |
123 | @override
124 | @HiveField(1)
125 | final String tString;
126 |
127 | @override
128 | @HiveField(2)
129 | final int tLong; // 64-bit
130 |
131 | final obxRelTarget = obx.ToOne();
132 |
133 | @override
134 | @obx.Transient()
135 | @HiveField(3)
136 | final int relTargetId;
137 |
138 | // Note: constructor arg types must match with fromMap used by sqflite.
139 | RelSourceEntityPlain(this.id, this.tString, this.tLong,
140 | [this.relTargetId = 0]) {
141 | obxRelTarget.targetId = relTargetId;
142 | }
143 |
144 | RelSourceEntityPlain.forInsert(
145 | this.tString, this.tLong, RelTargetEntity? relTarget)
146 | : id = 0,
147 | relTargetId = relTarget?.id ?? 0 {
148 | obxRelTarget.targetId = relTargetId;
149 | }
150 |
151 | static RelSourceEntityPlain fromMap(Map map) =>
152 | RelSourceEntityPlain(
153 | map['id'] ?? 0, map['tString'], map['tLong'], map['relTargetId']);
154 | }
155 |
156 | @obx.Entity()
157 | class RelSourceEntityIndexed implements RelSourceEntity {
158 | @override
159 | int id;
160 |
161 | @override
162 | @obx.Index()
163 | final String tString;
164 |
165 | @override
166 | final int tLong; // 64-bit
167 |
168 | final obxRelTarget = obx.ToOne();
169 |
170 | @override
171 | @obx.Transient()
172 | final int relTargetId;
173 |
174 | // Note: constructor arg types must match with fromMap used by sqflite.
175 | RelSourceEntityIndexed(this.id, this.tString, this.tLong,
176 | [this.relTargetId = 0]) {
177 | obxRelTarget.targetId = relTargetId;
178 | }
179 |
180 | RelSourceEntityIndexed.forInsert(
181 | this.tString, this.tLong, RelTargetEntity? relTarget)
182 | : id = 0,
183 | relTargetId = relTarget?.id ?? 0 {
184 | obxRelTarget.targetId = relTargetId;
185 | }
186 |
187 | static RelSourceEntityIndexed fromMap(Map map) =>
188 | RelSourceEntityIndexed(
189 | map['id'] ?? 0, map['tString'], map['tLong'], map['relTargetId']);
190 | }
191 |
192 | @obx.Entity()
193 | @HiveType(typeId: 4)
194 | class RelTargetEntity extends EntityWithSettableId {
195 | @override
196 | @HiveField(0)
197 | int id;
198 |
199 | @obx.Index()
200 | @HiveField(1)
201 | String name;
202 |
203 | RelTargetEntity(this.id, this.name);
204 |
205 | static Map toMap(RelTargetEntity object) =>
206 | {
207 | 'id': object.id == 0 ? null : object.id,
208 | 'name': object.name
209 | };
210 |
211 | static RelTargetEntity fromMap(Map map) =>
212 | RelTargetEntity(map['id'] ?? 0, map['name']);
213 | }
214 |
--------------------------------------------------------------------------------
/lib/model.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'model.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class TestEntityPlainAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 1;
12 |
13 | @override
14 | TestEntityPlain read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return TestEntityPlain(
20 | fields[0] as int,
21 | fields[1] as String,
22 | fields[2] as int,
23 | fields[3] as int,
24 | fields[4] as double,
25 | );
26 | }
27 |
28 | @override
29 | void write(BinaryWriter writer, TestEntityPlain obj) {
30 | writer
31 | ..writeByte(5)
32 | ..writeByte(0)
33 | ..write(obj.id)
34 | ..writeByte(1)
35 | ..write(obj.tString)
36 | ..writeByte(2)
37 | ..write(obj.tInt)
38 | ..writeByte(3)
39 | ..write(obj.tLong)
40 | ..writeByte(4)
41 | ..write(obj.tDouble);
42 | }
43 |
44 | @override
45 | int get hashCode => typeId.hashCode;
46 |
47 | @override
48 | bool operator ==(Object other) =>
49 | identical(this, other) ||
50 | other is TestEntityPlainAdapter &&
51 | runtimeType == other.runtimeType &&
52 | typeId == other.typeId;
53 | }
54 |
55 | class RelSourceEntityPlainAdapter extends TypeAdapter {
56 | @override
57 | final int typeId = 2;
58 |
59 | @override
60 | RelSourceEntityPlain read(BinaryReader reader) {
61 | final numOfFields = reader.readByte();
62 | final fields = {
63 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
64 | };
65 | return RelSourceEntityPlain(
66 | fields[0] as int,
67 | fields[1] as String,
68 | fields[2] as int,
69 | fields[3] as int,
70 | );
71 | }
72 |
73 | @override
74 | void write(BinaryWriter writer, RelSourceEntityPlain obj) {
75 | writer
76 | ..writeByte(4)
77 | ..writeByte(0)
78 | ..write(obj.id)
79 | ..writeByte(1)
80 | ..write(obj.tString)
81 | ..writeByte(2)
82 | ..write(obj.tLong)
83 | ..writeByte(3)
84 | ..write(obj.relTargetId);
85 | }
86 |
87 | @override
88 | int get hashCode => typeId.hashCode;
89 |
90 | @override
91 | bool operator ==(Object other) =>
92 | identical(this, other) ||
93 | other is RelSourceEntityPlainAdapter &&
94 | runtimeType == other.runtimeType &&
95 | typeId == other.typeId;
96 | }
97 |
98 | class RelTargetEntityAdapter extends TypeAdapter {
99 | @override
100 | final int typeId = 4;
101 |
102 | @override
103 | RelTargetEntity read(BinaryReader reader) {
104 | final numOfFields = reader.readByte();
105 | final fields = {
106 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
107 | };
108 | return RelTargetEntity(
109 | fields[0] as int,
110 | fields[1] as String,
111 | );
112 | }
113 |
114 | @override
115 | void write(BinaryWriter writer, RelTargetEntity obj) {
116 | writer
117 | ..writeByte(2)
118 | ..writeByte(0)
119 | ..write(obj.id)
120 | ..writeByte(1)
121 | ..write(obj.name);
122 | }
123 |
124 | @override
125 | int get hashCode => typeId.hashCode;
126 |
127 | @override
128 | bool operator ==(Object other) =>
129 | identical(this, other) ||
130 | other is RelTargetEntityAdapter &&
131 | runtimeType == other.runtimeType &&
132 | typeId == other.typeId;
133 | }
134 |
--------------------------------------------------------------------------------
/lib/objectbox-model.json:
--------------------------------------------------------------------------------
1 | {
2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
5 | "entities": [
6 | {
7 | "id": "2:5453298016524400943",
8 | "lastPropertyId": "5:8933220997389638466",
9 | "name": "TestEntityIndexed",
10 | "properties": [
11 | {
12 | "id": "1:6045856831974807584",
13 | "name": "id",
14 | "type": 6,
15 | "flags": 1
16 | },
17 | {
18 | "id": "2:6936912195487802902",
19 | "name": "tString",
20 | "type": 9,
21 | "flags": 2048,
22 | "indexId": "1:3627912493679987388"
23 | },
24 | {
25 | "id": "3:2740551240924923452",
26 | "name": "tInt",
27 | "type": 5,
28 | "flags": 8,
29 | "indexId": "2:155116997669208311"
30 | },
31 | {
32 | "id": "4:5923275911778414554",
33 | "name": "tLong",
34 | "type": 6
35 | },
36 | {
37 | "id": "5:8933220997389638466",
38 | "name": "tDouble",
39 | "type": 8
40 | }
41 | ],
42 | "relations": []
43 | },
44 | {
45 | "id": "3:5087630674825288337",
46 | "lastPropertyId": "5:2633769713742296285",
47 | "name": "TestEntityPlain",
48 | "properties": [
49 | {
50 | "id": "1:4900264929263122750",
51 | "name": "id",
52 | "type": 6,
53 | "flags": 1
54 | },
55 | {
56 | "id": "2:744298398557394245",
57 | "name": "tString",
58 | "type": 9
59 | },
60 | {
61 | "id": "3:8517120886012576776",
62 | "name": "tInt",
63 | "type": 5
64 | },
65 | {
66 | "id": "4:4690018982222267350",
67 | "name": "tLong",
68 | "type": 6
69 | },
70 | {
71 | "id": "5:2633769713742296285",
72 | "name": "tDouble",
73 | "type": 8
74 | }
75 | ],
76 | "relations": []
77 | },
78 | {
79 | "id": "4:3131756290746376924",
80 | "lastPropertyId": "4:3010204554547165657",
81 | "name": "RelSourceEntityIndexed",
82 | "properties": [
83 | {
84 | "id": "1:857073402609537123",
85 | "name": "id",
86 | "type": 6,
87 | "flags": 1
88 | },
89 | {
90 | "id": "2:8422124894128951003",
91 | "name": "tString",
92 | "type": 9,
93 | "flags": 2048,
94 | "indexId": "3:1267973680746353216"
95 | },
96 | {
97 | "id": "3:4831158543554459278",
98 | "name": "tLong",
99 | "type": 6
100 | },
101 | {
102 | "id": "4:3010204554547165657",
103 | "name": "obxRelTargetId",
104 | "type": 11,
105 | "flags": 520,
106 | "indexId": "4:1067902231777022849",
107 | "relationTarget": "RelTargetEntity"
108 | }
109 | ],
110 | "relations": []
111 | },
112 | {
113 | "id": "5:6999419589241537986",
114 | "lastPropertyId": "4:2940873808771910881",
115 | "name": "RelSourceEntityPlain",
116 | "properties": [
117 | {
118 | "id": "1:2178707517515063163",
119 | "name": "id",
120 | "type": 6,
121 | "flags": 1
122 | },
123 | {
124 | "id": "2:6759259138758744038",
125 | "name": "tString",
126 | "type": 9
127 | },
128 | {
129 | "id": "3:8644747147683565072",
130 | "name": "tLong",
131 | "type": 6
132 | },
133 | {
134 | "id": "4:2940873808771910881",
135 | "name": "obxRelTargetId",
136 | "type": 11,
137 | "flags": 520,
138 | "indexId": "5:1393076386577997042",
139 | "relationTarget": "RelTargetEntity"
140 | }
141 | ],
142 | "relations": []
143 | },
144 | {
145 | "id": "6:3460633979791451003",
146 | "lastPropertyId": "2:8889103133672686790",
147 | "name": "RelTargetEntity",
148 | "properties": [
149 | {
150 | "id": "1:8661863255381416471",
151 | "name": "id",
152 | "type": 6,
153 | "flags": 1
154 | },
155 | {
156 | "id": "2:8889103133672686790",
157 | "name": "name",
158 | "type": 9,
159 | "flags": 2048,
160 | "indexId": "6:1013178598434110616"
161 | }
162 | ],
163 | "relations": []
164 | }
165 | ],
166 | "lastEntityId": "6:3460633979791451003",
167 | "lastIndexId": "6:1013178598434110616",
168 | "lastRelationId": "0:0",
169 | "lastSequenceId": "0:0",
170 | "modelVersion": 5,
171 | "modelVersionParserMinimum": 5,
172 | "retiredEntityUids": [
173 | 1824294286181366916
174 | ],
175 | "retiredIndexUids": [],
176 | "retiredPropertyUids": [
177 | 725236837183566145,
178 | 3060128101942237916,
179 | 3092165041934597703,
180 | 4958684909838795382,
181 | 5062831200094422416
182 | ],
183 | "retiredRelationUids": [],
184 | "version": 1
185 | }
--------------------------------------------------------------------------------
/lib/objectbox.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // This code was generated by ObjectBox. To update it run the generator again
3 | // with `dart run build_runner build`.
4 | // See also https://docs.objectbox.io/getting-started#generate-objectbox-code
5 |
6 | // ignore_for_file: camel_case_types, depend_on_referenced_packages
7 | // coverage:ignore-file
8 |
9 | import 'dart:typed_data';
10 |
11 | import 'package:flat_buffers/flat_buffers.dart' as fb;
12 | import 'package:objectbox/internal.dart'
13 | as obx_int; // generated code can access "internal" functionality
14 | import 'package:objectbox/objectbox.dart' as obx;
15 | import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart';
16 |
17 | import 'model.dart';
18 |
19 | export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
20 |
21 | final _entities = [
22 | obx_int.ModelEntity(
23 | id: const obx_int.IdUid(2, 5453298016524400943),
24 | name: 'TestEntityIndexed',
25 | lastPropertyId: const obx_int.IdUid(5, 8933220997389638466),
26 | flags: 0,
27 | properties: [
28 | obx_int.ModelProperty(
29 | id: const obx_int.IdUid(1, 6045856831974807584),
30 | name: 'id',
31 | type: 6,
32 | flags: 1),
33 | obx_int.ModelProperty(
34 | id: const obx_int.IdUid(2, 6936912195487802902),
35 | name: 'tString',
36 | type: 9,
37 | flags: 2048,
38 | indexId: const obx_int.IdUid(1, 3627912493679987388)),
39 | obx_int.ModelProperty(
40 | id: const obx_int.IdUid(3, 2740551240924923452),
41 | name: 'tInt',
42 | type: 5,
43 | flags: 8,
44 | indexId: const obx_int.IdUid(2, 155116997669208311)),
45 | obx_int.ModelProperty(
46 | id: const obx_int.IdUid(4, 5923275911778414554),
47 | name: 'tLong',
48 | type: 6,
49 | flags: 0),
50 | obx_int.ModelProperty(
51 | id: const obx_int.IdUid(5, 8933220997389638466),
52 | name: 'tDouble',
53 | type: 8,
54 | flags: 0)
55 | ],
56 | relations: [],
57 | backlinks: []),
58 | obx_int.ModelEntity(
59 | id: const obx_int.IdUid(3, 5087630674825288337),
60 | name: 'TestEntityPlain',
61 | lastPropertyId: const obx_int.IdUid(5, 2633769713742296285),
62 | flags: 0,
63 | properties: [
64 | obx_int.ModelProperty(
65 | id: const obx_int.IdUid(1, 4900264929263122750),
66 | name: 'id',
67 | type: 6,
68 | flags: 1),
69 | obx_int.ModelProperty(
70 | id: const obx_int.IdUid(2, 744298398557394245),
71 | name: 'tString',
72 | type: 9,
73 | flags: 0),
74 | obx_int.ModelProperty(
75 | id: const obx_int.IdUid(3, 8517120886012576776),
76 | name: 'tInt',
77 | type: 5,
78 | flags: 0),
79 | obx_int.ModelProperty(
80 | id: const obx_int.IdUid(4, 4690018982222267350),
81 | name: 'tLong',
82 | type: 6,
83 | flags: 0),
84 | obx_int.ModelProperty(
85 | id: const obx_int.IdUid(5, 2633769713742296285),
86 | name: 'tDouble',
87 | type: 8,
88 | flags: 0)
89 | ],
90 | relations: [],
91 | backlinks: []),
92 | obx_int.ModelEntity(
93 | id: const obx_int.IdUid(4, 3131756290746376924),
94 | name: 'RelSourceEntityIndexed',
95 | lastPropertyId: const obx_int.IdUid(4, 3010204554547165657),
96 | flags: 0,
97 | properties: [
98 | obx_int.ModelProperty(
99 | id: const obx_int.IdUid(1, 857073402609537123),
100 | name: 'id',
101 | type: 6,
102 | flags: 1),
103 | obx_int.ModelProperty(
104 | id: const obx_int.IdUid(2, 8422124894128951003),
105 | name: 'tString',
106 | type: 9,
107 | flags: 2048,
108 | indexId: const obx_int.IdUid(3, 1267973680746353216)),
109 | obx_int.ModelProperty(
110 | id: const obx_int.IdUid(3, 4831158543554459278),
111 | name: 'tLong',
112 | type: 6,
113 | flags: 0),
114 | obx_int.ModelProperty(
115 | id: const obx_int.IdUid(4, 3010204554547165657),
116 | name: 'obxRelTargetId',
117 | type: 11,
118 | flags: 520,
119 | indexId: const obx_int.IdUid(4, 1067902231777022849),
120 | relationTarget: 'RelTargetEntity')
121 | ],
122 | relations: [],
123 | backlinks: []),
124 | obx_int.ModelEntity(
125 | id: const obx_int.IdUid(5, 6999419589241537986),
126 | name: 'RelSourceEntityPlain',
127 | lastPropertyId: const obx_int.IdUid(4, 2940873808771910881),
128 | flags: 0,
129 | properties: [
130 | obx_int.ModelProperty(
131 | id: const obx_int.IdUid(1, 2178707517515063163),
132 | name: 'id',
133 | type: 6,
134 | flags: 1),
135 | obx_int.ModelProperty(
136 | id: const obx_int.IdUid(2, 6759259138758744038),
137 | name: 'tString',
138 | type: 9,
139 | flags: 0),
140 | obx_int.ModelProperty(
141 | id: const obx_int.IdUid(3, 8644747147683565072),
142 | name: 'tLong',
143 | type: 6,
144 | flags: 0),
145 | obx_int.ModelProperty(
146 | id: const obx_int.IdUid(4, 2940873808771910881),
147 | name: 'obxRelTargetId',
148 | type: 11,
149 | flags: 520,
150 | indexId: const obx_int.IdUid(5, 1393076386577997042),
151 | relationTarget: 'RelTargetEntity')
152 | ],
153 | relations: [],
154 | backlinks: []),
155 | obx_int.ModelEntity(
156 | id: const obx_int.IdUid(6, 3460633979791451003),
157 | name: 'RelTargetEntity',
158 | lastPropertyId: const obx_int.IdUid(2, 8889103133672686790),
159 | flags: 0,
160 | properties: [
161 | obx_int.ModelProperty(
162 | id: const obx_int.IdUid(1, 8661863255381416471),
163 | name: 'id',
164 | type: 6,
165 | flags: 1),
166 | obx_int.ModelProperty(
167 | id: const obx_int.IdUid(2, 8889103133672686790),
168 | name: 'name',
169 | type: 9,
170 | flags: 2048,
171 | indexId: const obx_int.IdUid(6, 1013178598434110616))
172 | ],
173 | relations: [],
174 | backlinks: [])
175 | ];
176 |
177 | /// Shortcut for [obx.Store.new] that passes [getObjectBoxModel] and for Flutter
178 | /// apps by default a [directory] using `defaultStoreDirectory()` from the
179 | /// ObjectBox Flutter library.
180 | ///
181 | /// Note: for desktop apps it is recommended to specify a unique [directory].
182 | ///
183 | /// See [obx.Store.new] for an explanation of all parameters.
184 | ///
185 | /// For Flutter apps, also calls `loadObjectBoxLibraryAndroidCompat()` from
186 | /// the ObjectBox Flutter library to fix loading the native ObjectBox library
187 | /// on Android 6 and older.
188 | Future openStore(
189 | {String? directory,
190 | int? maxDBSizeInKB,
191 | int? maxDataSizeInKB,
192 | int? fileMode,
193 | int? maxReaders,
194 | bool queriesCaseSensitiveDefault = true,
195 | String? macosApplicationGroup}) async {
196 | await loadObjectBoxLibraryAndroidCompat();
197 | return obx.Store(getObjectBoxModel(),
198 | directory: directory ?? (await defaultStoreDirectory()).path,
199 | maxDBSizeInKB: maxDBSizeInKB,
200 | maxDataSizeInKB: maxDataSizeInKB,
201 | fileMode: fileMode,
202 | maxReaders: maxReaders,
203 | queriesCaseSensitiveDefault: queriesCaseSensitiveDefault,
204 | macosApplicationGroup: macosApplicationGroup);
205 | }
206 |
207 | /// Returns the ObjectBox model definition for this project for use with
208 | /// [obx.Store.new].
209 | obx_int.ModelDefinition getObjectBoxModel() {
210 | final model = obx_int.ModelInfo(
211 | entities: _entities,
212 | lastEntityId: const obx_int.IdUid(6, 3460633979791451003),
213 | lastIndexId: const obx_int.IdUid(6, 1013178598434110616),
214 | lastRelationId: const obx_int.IdUid(0, 0),
215 | lastSequenceId: const obx_int.IdUid(0, 0),
216 | retiredEntityUids: const [1824294286181366916],
217 | retiredIndexUids: const [],
218 | retiredPropertyUids: const [
219 | 725236837183566145,
220 | 3060128101942237916,
221 | 3092165041934597703,
222 | 4958684909838795382,
223 | 5062831200094422416
224 | ],
225 | retiredRelationUids: const [],
226 | modelVersion: 5,
227 | modelVersionParserMinimum: 5,
228 | version: 1);
229 |
230 | final bindings = {
231 | TestEntityIndexed: obx_int.EntityDefinition(
232 | model: _entities[0],
233 | toOneRelations: (TestEntityIndexed object) => [],
234 | toManyRelations: (TestEntityIndexed object) => {},
235 | getId: (TestEntityIndexed object) => object.id,
236 | setId: (TestEntityIndexed object, int id) {
237 | object.id = id;
238 | },
239 | objectToFB: (TestEntityIndexed object, fb.Builder fbb) {
240 | final tStringOffset = fbb.writeString(object.tString);
241 | fbb.startTable(6);
242 | fbb.addInt64(0, object.id);
243 | fbb.addOffset(1, tStringOffset);
244 | fbb.addInt32(2, object.tInt);
245 | fbb.addInt64(3, object.tLong);
246 | fbb.addFloat64(4, object.tDouble);
247 | fbb.finish(fbb.endTable());
248 | return object.id;
249 | },
250 | objectFromFB: (obx.Store store, ByteData fbData) {
251 | final buffer = fb.BufferContext(fbData);
252 | final rootOffset = buffer.derefObject(0);
253 | final idParam =
254 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0);
255 | final tStringParam = const fb.StringReader(asciiOptimization: true)
256 | .vTableGet(buffer, rootOffset, 6, '');
257 | final tIntParam =
258 | const fb.Int32Reader().vTableGet(buffer, rootOffset, 8, 0);
259 | final tLongParam =
260 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
261 | final tDoubleParam =
262 | const fb.Float64Reader().vTableGet(buffer, rootOffset, 12, 0);
263 | final object = TestEntityIndexed(
264 | idParam, tStringParam, tIntParam, tLongParam, tDoubleParam);
265 |
266 | return object;
267 | }),
268 | TestEntityPlain: obx_int.EntityDefinition(
269 | model: _entities[1],
270 | toOneRelations: (TestEntityPlain object) => [],
271 | toManyRelations: (TestEntityPlain object) => {},
272 | getId: (TestEntityPlain object) => object.id,
273 | setId: (TestEntityPlain object, int id) {
274 | object.id = id;
275 | },
276 | objectToFB: (TestEntityPlain object, fb.Builder fbb) {
277 | final tStringOffset = fbb.writeString(object.tString);
278 | fbb.startTable(6);
279 | fbb.addInt64(0, object.id);
280 | fbb.addOffset(1, tStringOffset);
281 | fbb.addInt32(2, object.tInt);
282 | fbb.addInt64(3, object.tLong);
283 | fbb.addFloat64(4, object.tDouble);
284 | fbb.finish(fbb.endTable());
285 | return object.id;
286 | },
287 | objectFromFB: (obx.Store store, ByteData fbData) {
288 | final buffer = fb.BufferContext(fbData);
289 | final rootOffset = buffer.derefObject(0);
290 | final idParam =
291 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0);
292 | final tStringParam = const fb.StringReader(asciiOptimization: true)
293 | .vTableGet(buffer, rootOffset, 6, '');
294 | final tIntParam =
295 | const fb.Int32Reader().vTableGet(buffer, rootOffset, 8, 0);
296 | final tLongParam =
297 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
298 | final tDoubleParam =
299 | const fb.Float64Reader().vTableGet(buffer, rootOffset, 12, 0);
300 | final object = TestEntityPlain(
301 | idParam, tStringParam, tIntParam, tLongParam, tDoubleParam);
302 |
303 | return object;
304 | }),
305 | RelSourceEntityIndexed: obx_int.EntityDefinition(
306 | model: _entities[2],
307 | toOneRelations: (RelSourceEntityIndexed object) =>
308 | [object.obxRelTarget],
309 | toManyRelations: (RelSourceEntityIndexed object) => {},
310 | getId: (RelSourceEntityIndexed object) => object.id,
311 | setId: (RelSourceEntityIndexed object, int id) {
312 | object.id = id;
313 | },
314 | objectToFB: (RelSourceEntityIndexed object, fb.Builder fbb) {
315 | final tStringOffset = fbb.writeString(object.tString);
316 | fbb.startTable(5);
317 | fbb.addInt64(0, object.id);
318 | fbb.addOffset(1, tStringOffset);
319 | fbb.addInt64(2, object.tLong);
320 | fbb.addInt64(3, object.obxRelTarget.targetId);
321 | fbb.finish(fbb.endTable());
322 | return object.id;
323 | },
324 | objectFromFB: (obx.Store store, ByteData fbData) {
325 | final buffer = fb.BufferContext(fbData);
326 | final rootOffset = buffer.derefObject(0);
327 | final idParam =
328 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0);
329 | final tStringParam = const fb.StringReader(asciiOptimization: true)
330 | .vTableGet(buffer, rootOffset, 6, '');
331 | final tLongParam =
332 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0);
333 | final object =
334 | RelSourceEntityIndexed(idParam, tStringParam, tLongParam);
335 | object.obxRelTarget.targetId =
336 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
337 | object.obxRelTarget.attach(store);
338 | return object;
339 | }),
340 | RelSourceEntityPlain: obx_int.EntityDefinition(
341 | model: _entities[3],
342 | toOneRelations: (RelSourceEntityPlain object) => [object.obxRelTarget],
343 | toManyRelations: (RelSourceEntityPlain object) => {},
344 | getId: (RelSourceEntityPlain object) => object.id,
345 | setId: (RelSourceEntityPlain object, int id) {
346 | object.id = id;
347 | },
348 | objectToFB: (RelSourceEntityPlain object, fb.Builder fbb) {
349 | final tStringOffset = fbb.writeString(object.tString);
350 | fbb.startTable(5);
351 | fbb.addInt64(0, object.id);
352 | fbb.addOffset(1, tStringOffset);
353 | fbb.addInt64(2, object.tLong);
354 | fbb.addInt64(3, object.obxRelTarget.targetId);
355 | fbb.finish(fbb.endTable());
356 | return object.id;
357 | },
358 | objectFromFB: (obx.Store store, ByteData fbData) {
359 | final buffer = fb.BufferContext(fbData);
360 | final rootOffset = buffer.derefObject(0);
361 | final idParam =
362 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0);
363 | final tStringParam = const fb.StringReader(asciiOptimization: true)
364 | .vTableGet(buffer, rootOffset, 6, '');
365 | final tLongParam =
366 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0);
367 | final object =
368 | RelSourceEntityPlain(idParam, tStringParam, tLongParam);
369 | object.obxRelTarget.targetId =
370 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
371 | object.obxRelTarget.attach(store);
372 | return object;
373 | }),
374 | RelTargetEntity: obx_int.EntityDefinition(
375 | model: _entities[4],
376 | toOneRelations: (RelTargetEntity object) => [],
377 | toManyRelations: (RelTargetEntity object) => {},
378 | getId: (RelTargetEntity object) => object.id,
379 | setId: (RelTargetEntity object, int id) {
380 | object.id = id;
381 | },
382 | objectToFB: (RelTargetEntity object, fb.Builder fbb) {
383 | final nameOffset = fbb.writeString(object.name);
384 | fbb.startTable(3);
385 | fbb.addInt64(0, object.id);
386 | fbb.addOffset(1, nameOffset);
387 | fbb.finish(fbb.endTable());
388 | return object.id;
389 | },
390 | objectFromFB: (obx.Store store, ByteData fbData) {
391 | final buffer = fb.BufferContext(fbData);
392 | final rootOffset = buffer.derefObject(0);
393 | final idParam =
394 | const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0);
395 | final nameParam = const fb.StringReader(asciiOptimization: true)
396 | .vTableGet(buffer, rootOffset, 6, '');
397 | final object = RelTargetEntity(idParam, nameParam);
398 |
399 | return object;
400 | })
401 | };
402 |
403 | return obx_int.ModelDefinition(model, bindings);
404 | }
405 |
406 | /// [TestEntityIndexed] entity fields to define ObjectBox queries.
407 | class TestEntityIndexed_ {
408 | /// See [TestEntityIndexed.id].
409 | static final id =
410 | obx.QueryIntegerProperty(_entities[0].properties[0]);
411 |
412 | /// See [TestEntityIndexed.tString].
413 | static final tString =
414 | obx.QueryStringProperty(_entities[0].properties[1]);
415 |
416 | /// See [TestEntityIndexed.tInt].
417 | static final tInt =
418 | obx.QueryIntegerProperty(_entities[0].properties[2]);
419 |
420 | /// See [TestEntityIndexed.tLong].
421 | static final tLong =
422 | obx.QueryIntegerProperty(_entities[0].properties[3]);
423 |
424 | /// See [TestEntityIndexed.tDouble].
425 | static final tDouble =
426 | obx.QueryDoubleProperty(_entities[0].properties[4]);
427 | }
428 |
429 | /// [TestEntityPlain] entity fields to define ObjectBox queries.
430 | class TestEntityPlain_ {
431 | /// See [TestEntityPlain.id].
432 | static final id =
433 | obx.QueryIntegerProperty(_entities[1].properties[0]);
434 |
435 | /// See [TestEntityPlain.tString].
436 | static final tString =
437 | obx.QueryStringProperty(_entities[1].properties[1]);
438 |
439 | /// See [TestEntityPlain.tInt].
440 | static final tInt =
441 | obx.QueryIntegerProperty(_entities[1].properties[2]);
442 |
443 | /// See [TestEntityPlain.tLong].
444 | static final tLong =
445 | obx.QueryIntegerProperty(_entities[1].properties[3]);
446 |
447 | /// See [TestEntityPlain.tDouble].
448 | static final tDouble =
449 | obx.QueryDoubleProperty(_entities[1].properties[4]);
450 | }
451 |
452 | /// [RelSourceEntityIndexed] entity fields to define ObjectBox queries.
453 | class RelSourceEntityIndexed_ {
454 | /// See [RelSourceEntityIndexed.id].
455 | static final id = obx.QueryIntegerProperty(
456 | _entities[2].properties[0]);
457 |
458 | /// See [RelSourceEntityIndexed.tString].
459 | static final tString = obx.QueryStringProperty(
460 | _entities[2].properties[1]);
461 |
462 | /// See [RelSourceEntityIndexed.tLong].
463 | static final tLong = obx.QueryIntegerProperty(
464 | _entities[2].properties[2]);
465 |
466 | /// See [RelSourceEntityIndexed.obxRelTarget].
467 | static final obxRelTarget =
468 | obx.QueryRelationToOne(
469 | _entities[2].properties[3]);
470 | }
471 |
472 | /// [RelSourceEntityPlain] entity fields to define ObjectBox queries.
473 | class RelSourceEntityPlain_ {
474 | /// See [RelSourceEntityPlain.id].
475 | static final id = obx.QueryIntegerProperty(
476 | _entities[3].properties[0]);
477 |
478 | /// See [RelSourceEntityPlain.tString].
479 | static final tString =
480 | obx.QueryStringProperty(_entities[3].properties[1]);
481 |
482 | /// See [RelSourceEntityPlain.tLong].
483 | static final tLong = obx.QueryIntegerProperty(
484 | _entities[3].properties[2]);
485 |
486 | /// See [RelSourceEntityPlain.obxRelTarget].
487 | static final obxRelTarget =
488 | obx.QueryRelationToOne(
489 | _entities[3].properties[3]);
490 | }
491 |
492 | /// [RelTargetEntity] entity fields to define ObjectBox queries.
493 | class RelTargetEntity_ {
494 | /// See [RelTargetEntity.id].
495 | static final id =
496 | obx.QueryIntegerProperty(_entities[4].properties[0]);
497 |
498 | /// See [RelTargetEntity.name].
499 | static final name =
500 | obx.QueryStringProperty(_entities[4].properties[1]);
501 | }
502 |
--------------------------------------------------------------------------------
/lib/obx_executor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'executor.dart';
4 | import 'model.dart';
5 | import 'objectbox.g.dart';
6 | import 'time_tracker.dart';
7 |
8 | abstract class Executor extends ExecutorBase {
9 | final Store store;
10 | final Box box;
11 | final Query queryStringEq;
12 | final void Function(String) queryStringEqSetValue;
13 |
14 | Executor._(
15 | super.tracker, this.store, this.queryStringEq, this.queryStringEqSetValue)
16 | : box = store.box();
17 |
18 | @override
19 | Future close() async => store.close();
20 |
21 | @override
22 | Future insertMany(List items) =>
23 | Future.value(tracker.track('insertMany', () => box.putMany(items)));
24 |
25 | @override
26 | Future updateMany(List items) =>
27 | Future.value(tracker.track('updateMany', () => box.putMany(items)));
28 |
29 | @override
30 | Future> readAll(List optionalIds) =>
31 | Future.value(tracker.track('readAll', () => box.getAll()));
32 |
33 | @override
34 | Future> queryById(List ids, [String? benchmarkQualifier]) =>
35 | Future.value(tracker.track(
36 | 'queryById${benchmarkQualifier ?? ''}', () => box.getMany(ids)));
37 |
38 | @override
39 | Future removeMany(List ids) =>
40 | Future.value(tracker.track('removeMany', () => box.removeMany(ids)));
41 |
42 | @override
43 | Future> queryStringEquals(List values) =>
44 | Future.value(tracker.track(
45 | 'queryStringEquals',
46 | () => store.runInTransaction(TxMode.read, () {
47 | late List result;
48 | final length = values.length;
49 | for (var i = 0; i < length; i++) {
50 | queryStringEqSetValue(values[i]);
51 | result = queryStringEq.find();
52 | }
53 | return result;
54 | })));
55 | }
56 |
57 | /// Using an entity without indexes
58 | class ExecutorPlain extends Executor {
59 | ExecutorPlain._(super.tracker, super.store, super.queryStringEq,
60 | super.queryStringEqSetValue)
61 | : super._();
62 |
63 | factory ExecutorPlain(Directory dbDir, TimeTracker tracker) {
64 | final store = Store(getObjectBoxModel(),
65 | directory: dbDir.path,
66 | queriesCaseSensitiveDefault: ExecutorBase.caseSensitive);
67 |
68 | final query = store
69 | .box()
70 | .query(TestEntityPlain_.tString.equals(''))
71 | .build();
72 | final queryParam = query.param(TestEntityPlain_.tString);
73 | queryStringEqSetValue(String val) => queryParam.value = val;
74 |
75 | return ExecutorPlain._(tracker, store, query, queryStringEqSetValue);
76 | }
77 |
78 | @override
79 | TestEntityPlain createEntity(
80 | String tString, int tInt, int tLong, double tDouble) {
81 | return TestEntityPlain(0, tString, tInt, tLong, tDouble);
82 | }
83 |
84 | @override
85 | Future createRelBenchmark() =>
86 | Future.value(ExecutorRel(store, tracker));
87 | }
88 |
89 | /// Using an entity with indexes
90 | class ExecutorIndexed extends Executor {
91 | ExecutorIndexed._(super.tracker, super.store, super.queryStringEq,
92 | super.queryStringEqSetValue)
93 | : super._();
94 |
95 | factory ExecutorIndexed(Directory dbDir, TimeTracker tracker) {
96 | final store = Store(getObjectBoxModel(),
97 | directory: dbDir.path,
98 | queriesCaseSensitiveDefault: ExecutorBase.caseSensitive);
99 |
100 | final query = store
101 | .box()
102 | .query(TestEntityIndexed_.tString.equals(''))
103 | .build();
104 | final queryParam = query.param(TestEntityIndexed_.tString);
105 | queryStringEqSetValue(String val) => queryParam.value = val;
106 |
107 | return ExecutorIndexed._(tracker, store, query, queryStringEqSetValue);
108 | }
109 |
110 | @override
111 | TestEntityIndexed createEntity(
112 | String tString, int tInt, int tLong, double tDouble) {
113 | return TestEntityIndexed(0, tString, tInt, tLong, tDouble);
114 | }
115 |
116 | @override
117 | Future createRelBenchmark() =>
118 | Future.value(ExecutorRel(store, tracker));
119 | }
120 |
121 | class ExecutorRel extends ExecutorBaseRel {
122 | final Store store;
123 | final Box box;
124 | final Query query;
125 | final void Function(String, int, String) querySetParams;
126 |
127 | factory ExecutorRel(Store store, TimeTracker tracker) {
128 | late final void Function(String, int, String) querySetParams;
129 | late final Query queryT;
130 | if (T == RelSourceEntityPlain) {
131 | final query = (store.box().query(
132 | RelSourceEntityPlain_.tString.equals('') &
133 | RelSourceEntityPlain_.tLong.equals(0))
134 | ..link(RelSourceEntityPlain_.obxRelTarget,
135 | RelTargetEntity_.name.equals('')))
136 | .build();
137 | final queryParam1 = query.param(RelSourceEntityPlain_.tString);
138 | final queryParam2 = query.param(RelSourceEntityPlain_.tLong);
139 | final queryParam3 = query.param(RelTargetEntity_.name);
140 | querySetParams = (String sourceStringEquals, int sourceIntEquals,
141 | String targetStringEquals) {
142 | queryParam1.value = sourceStringEquals;
143 | queryParam2.value = sourceIntEquals;
144 | queryParam3.value = targetStringEquals;
145 | };
146 | queryT = query as Query;
147 | } else {
148 | final query = (store.box().query(
149 | RelSourceEntityIndexed_.tString.equals('') &
150 | RelSourceEntityIndexed_.tLong.equals(0))
151 | ..link(RelSourceEntityIndexed_.obxRelTarget,
152 | RelTargetEntity_.name.equals('')))
153 | .build();
154 | final queryParam1 = query.param(RelSourceEntityIndexed_.tString);
155 | final queryParam2 = query.param(RelSourceEntityIndexed_.tLong);
156 | final queryParam3 = query.param(RelTargetEntity_.name);
157 | querySetParams = (String sourceStringEquals, int sourceIntEquals,
158 | String targetStringEquals) {
159 | queryParam1.value = sourceStringEquals;
160 | queryParam2.value = sourceIntEquals;
161 | queryParam3.value = targetStringEquals;
162 | };
163 | queryT = query as Query;
164 | }
165 | return ExecutorRel._(tracker, store, queryT, querySetParams);
166 | }
167 |
168 | ExecutorRel._(super.tracker, this.store, this.query, this.querySetParams)
169 | : box = store.box();
170 |
171 | @override
172 | Future close() async {
173 | // Do nothing, store is closed by Executor.
174 | }
175 |
176 | @override
177 | Future insertData(int relSourceCount, int relTargetCount) =>
178 | Future.sync(() {
179 | final targets = prepareDataTargets(relTargetCount);
180 | store.box().putMany(targets);
181 | final sources = prepareDataSources(relSourceCount, targets);
182 | box.putMany(sources);
183 | assert(box.count() == relSourceCount);
184 | assert(store.box().count() == relTargetCount);
185 | });
186 |
187 | @override
188 | Future> queryWithLinks(List args) =>
189 | Future.value(tracker.track(
190 | 'queryWithLinks',
191 | () => store.runInTransaction(TxMode.read, () {
192 | late List result;
193 | final length = args.length;
194 | for (var i = 0; i < length; i++) {
195 | querySetParams(args[i].sourceStringEquals,
196 | args[i].sourceIntEquals, args[i].targetStringEquals);
197 | result = query.find();
198 | }
199 | return result;
200 | })));
201 | }
202 |
--------------------------------------------------------------------------------
/lib/sqf_executor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:sqflite/sqflite.dart';
4 |
5 | import 'executor.dart';
6 | import 'model.dart';
7 | import 'time_tracker.dart';
8 |
9 | abstract class Executor extends ExecutorBase {
10 | final Database _db;
11 | final String _table;
12 |
13 | final T Function(Map) _fromMap;
14 |
15 | Executor._(this._table, this._db, this._fromMap, TimeTracker tracker)
16 | : super(tracker);
17 |
18 | static Future createDatabase(
19 | Directory dbDir, String table, bool withIndexes) {
20 | return openDatabase(dbDir.path, version: 1,
21 | onCreate: (Database db, int version) async {
22 | await db.execute('''
23 | CREATE TABLE $table (
24 | id integer primary key autoincrement,
25 | tString text,
26 | tInt int,
27 | tLong int,
28 | tDouble real)
29 | ''');
30 | if (withIndexes) {
31 | await db.execute('CREATE INDEX ${table}_int ON $table(tInt)');
32 | await db.execute('CREATE INDEX ${table}_str ON $table(tString '
33 | '${ExecutorBase.caseSensitive ? '' : 'COLLATE NOCASE'})');
34 | }
35 | });
36 | }
37 |
38 | @override
39 | Future close() => _db.close();
40 |
41 | // TODO use the generic _insertMany() if it doesn't decrease performance
42 | @override
43 | Future insertMany(List items) async =>
44 | tracker.trackAsync('insertMany', () async {
45 | final tx = _db.batch();
46 | for (var object in items) {
47 | tx.insert(_table, TestEntity.toMap(object));
48 | }
49 | final ids = await tx.commit();
50 | for (int i = 0; i < ids.length; i++) {
51 | items[i].id = ids[i] as int;
52 | }
53 | });
54 |
55 | @override
56 | Future updateMany(List items) async =>
57 | tracker.trackAsync('updateMany', () async {
58 | final tx = _db.batch();
59 | for (var object in items) {
60 | tx.update(_table, TestEntity.toMap(object),
61 | where: 'id = ?', whereArgs: [object.id]);
62 | }
63 | await tx.commit();
64 | });
65 |
66 | Future> _query(DatabaseExecutor db,
67 | T Function(Map) reader, String where,
68 | [List