[
  {
    "path": ".all-contributorsrc",
    "content": "{\n  \"files\": [\n    \"packages/isar/README.md\"\n  ],\n  \"imageSize\": 100,\n  \"commit\": false,\n  \"contributors\": [\n    {\n      \"login\": \"Jtplouffe\",\n      \"name\": \"JT\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/32107801?v=4\",\n      \"profile\": \"https://github.com/Jtplouffe\",\n      \"contributions\": [\n        \"test\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"leisim\",\n      \"name\": \"Simon Leier\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/13610195?v=4\",\n      \"profile\": \"https://www.linkedin.com/in/simon-leier/\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"doc\",\n        \"test\",\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"h1376h\",\n      \"name\": \"Hamed H.\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/3498335?v=4\",\n      \"profile\": \"https://github.com/h1376h\",\n      \"contributions\": [\n        \"code\",\n        \"maintenance\"\n      ]\n    },\n    {\n      \"login\": \"Viper-Bit\",\n      \"name\": \"Peyman\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/24822764?v=4\",\n      \"profile\": \"https://github.com/Viper-Bit\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"blendthink\",\n      \"name\": \"blendthink\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/32213113?v=4\",\n      \"profile\": \"https://github.com/blendthink\",\n      \"contributions\": [\n        \"maintenance\"\n      ]\n    },\n    {\n      \"login\": \"Moseco\",\n      \"name\": \"Moseco\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/10720298?v=4\",\n      \"profile\": \"https://github.com/Moseco\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"Frostedfox\",\n      \"name\": \"Frostedfox\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/84601232?v=4\",\n      \"profile\": \"https://github.com/Frostedfox\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"nohli\",\n      \"name\": \"Joachim Nohl\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/43643339?v=4\",\n      \"profile\": \"http://achim.io\",\n      \"contributions\": [\n        \"maintenance\"\n      ]\n    },\n    {\n      \"login\": \"VoidxHoshi\",\n      \"name\": \"LaLucid\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/55886143?v=4\",\n      \"profile\": \"https://github.com/VoidxHoshi\",\n      \"contributions\": [\n        \"maintenance\"\n      ]\n    },\n    {\n      \"login\": \"vothvovo\",\n      \"name\": \"Johnson\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/20894472?v=4\",\n      \"profile\": \"https://github.com/vothvovo\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"ika020202\",\n      \"name\": \"Ura\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/42883378?v=4\",\n      \"profile\": \"https://zenn.dev/urasan\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"mnkeis\",\n      \"name\": \"mnkeis\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/41247357?v=4\",\n      \"profile\": \"https://github.com/mnkeis\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"CarloDotLog\",\n      \"name\": \"Carlo Loguercio\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/13763473?v=4\",\n      \"profile\": \"https://github.com/CarloDotLog\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"hafeezrana\",\n      \"name\": \"Hafeez Rana\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/87476445?v=4\",\n      \"profile\": \"https://g.dev/hafeezrana\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"inkomomutane\",\n      \"name\": \"Nelson  Mutane\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/57417802?v=4\",\n      \"profile\": \"https://github.com/inkomomutane\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"lodisy\",\n      \"name\": \"Michael\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/8101584?v=4\",\n      \"profile\": \"https://github.com/lodisy\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"ritksm\",\n      \"name\": \"Jack Rivers\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/111809?v=4\",\n      \"profile\": \"http://blog.jackrivers.me/\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"buraktabn\",\n      \"name\": \"Burak\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/49204989?v=4\",\n      \"profile\": \"http://buraktaban.ca\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"AlexisL61\",\n      \"name\": \"Alexis\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/30233189?v=4\",\n      \"profile\": \"https://github.com/AlexisL61\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"letyletylety\",\n      \"name\": \"Lety\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/16468579?v=4\",\n      \"profile\": \"https://letyarch.blogspot.com/\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"nobkd\",\n      \"name\": \"nobkd\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/44443899?v=4\",\n      \"profile\": \"https://github.com/nobkd\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    }\n  ],\n  \"contributorTemplate\": \"<a href=\\\"https://github.com/<%= contributor.login %>\\\"><img src=\\\"<%= contributor.avatar_url %>\\\" width=\\\"<%= options.imageSize %>px;\\\" alt=\\\"\\\"/><br /><sub><b><%= contributor.name %></b></sub></a>\",\n  \"contributorsPerLine\": 7,\n  \"contributorsSortAlphabetically\": true,\n  \"projectName\": \"isar\",\n  \"projectOwner\": \"isar\",\n  \"repoType\": \"github\",\n  \"repoHost\": \"https://github.com\",\n  \"skipCi\": true,\n  \"commitConvention\": \"angular\"\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: simc\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: problem\nassignees: leisim\n\n---\n\n\n\n### Steps to Reproduce\n\nPlease describe exactly how to reproduce the problem you are running into.\n\n\n\n### Code sample\n\n```dart\nProvide a few simple lines of code to show your problem.\n```\n\n\n\n### Details\n\n - Platform: iPhone 13 Pro, Galaxy S7, x86 Android Emulator on Windows etc.\n - Flutter version: [e.g. 3.0.0]\n - Isar version: [e.g. 2.5.0]\n\n\n---\n\n\n - [ ] I searched for similar issues already\n - [ ] I filled the details section with the exact device model and version\n - [ ] I am able to provide a reproducible example\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question\n    url: https://github.com/isar-community/isar/discussions\n    about: Ask questions and discuss with other community members\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Version**\n - Platform: iOS, Android, Mac, Windows, Linux, Web\n - Flutter version: [e.g. 1.5.4]\n - Isar version: [e.g. 0.5.0]"
  },
  {
    "path": ".github/actions/prepare-build/action.yaml",
    "content": "name: \"Prepare Build\"\ndescription: \"Prepares the build for Isar Core\"\nruns:\n  using: \"composite\"\n  steps:\n    - name: Install LLVM and Clang\n      if: runner.os == 'Windows'\n      uses: KyleMayes/install-llvm-action@v1\n      with:\n        version: \"11.0\"\n        directory: ${{ runner.temp }}/llvm\n    - name: Set LIBCLANG_PATH\n      if: runner.os == 'Windows'\n      shell: bash\n      run: echo \"LIBCLANG_PATH=$((gcm clang).source -replace \"clang.exe\")\" >> $env:GITHUB_ENV\n    # See https://github.com/godot-rust/godot-rust/pull/920\n    - name: \"Workaround Android NDK due to Rust bug\"\n      if: runner.os == 'Linux' || runner.os == 'macOS'\n      shell: bash\n      run: >\n        find -L $ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION -name libunwind.a\n        -execdir sh -c 'echo \"INPUT(-lunwind)\" > libgcc.a' \\;\n"
  },
  {
    "path": ".github/dependabot.yaml",
    "content": "version: 2\nenable-beta-ecosystems: true\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"cargo\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"pub\"\n    directory: \"/packages/isar\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"pub\"\n    directory: \"/packages/isar_flutter_libs\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"pub\"\n    directory: \"/packages/isar_generator\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"pub\"\n    directory: \"/packages/isar_inspector\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"pub\"\n    directory: \"/packages/isar_test\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"pub\"\n    directory: \"/examples/pub\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"gradle\"\n    directory: \"/packages/isar_flutter_libs/android\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"gradle\"\n    directory: \"/packages/isar_test/android\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"npm\"\n    directory: \"/docs\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n  - package-ecosystem: \"npm\"\n    directory: \"/packages/isar_web\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"leisim\"\n"
  },
  {
    "path": ".github/workflows/cron_test.yaml",
    "content": "name: Dart CI Cron\n\non:\n  schedule:\n    - cron: \"0 0 * * 0\"\n\njobs:\n  testlab:\n    uses: ./.github/workflows/testlab.yaml\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/docs.yaml",
    "content": "name: Unified Deploy Docs\n\non:\n  push:\n    branches:\n      - main\n      - v3\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    permissions:\n      contents: write\n      id-token: write\n      pages: write\n    steps:\n      - name: Checkout v3 branch\n        uses: actions/checkout@v4\n        with:\n          ref: \"v3\"\n          path: \"v3\"\n\n      - name: Build v3 docs\n        run: |\n          cd v3\n          git fetch --unshallow\n          git fetch --tags\n          tool/replace-versions.sh\n          cd docs\n          sed -i'.bak' \"s|base:.*|base: '/v3/',|\" docs/.vuepress/config.ts\n          sed -i 's|text: \"vx.x\"|text: \"v3.x\"|' docs/.vuepress/config.ts  \n          npm ci\n          npm run build\n          mv ./docs/.vuepress/dist ../../v3-docs\n\n      - name: Checkout main branch\n        uses: actions/checkout@v4\n        with:\n          ref: \"main\"\n          path: \"main\"\n\n      - name: Build main docs\n        run: |\n          cd main\n          git fetch --tags\n          tool/replace-versions.sh\n          cd docs\n          sed -i'.bak' \"s|base:.*|base: '/',|\" docs/.vuepress/config.ts\n          sed -i 's|text: \"vx.x\"|text: \"v4.x\"|' docs/.vuepress/config.ts  \n          npm ci\n          npm run build\n          mv ./docs/.vuepress/dist ../../main-docs\n\n      - name: Prepare deployment directory\n        run: |\n          mkdir deploy\n          mv main-docs/* deploy/\n          mkdir deploy/v3\n          mv v3-docs/* deploy/v3/\n\n      - name: Setup Pages\n        uses: actions/configure-pages@v4\n\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: deploy\n\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4"
  },
  {
    "path": ".github/workflows/release.yaml",
    "content": "name: Isar release\n\non:\n  push:\n    tags:\n      - \"*\"\n\njobs:\n  verify_version:\n    name: Verify version matches release\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        sdk: [3.0.0]\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Verify release version\n        run: |\n          flutter pub get\n          dart tool/verify_release_version.dart ${{ github.ref_name }}\n        working-directory: packages/isar\n\n  build_binaries:\n    name: Build Binaries\n    needs: verify_version\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: ubuntu-latest\n            artifact_name: libisar_android_arm64.so\n            script: build_android.sh\n          - os: ubuntu-latest\n            artifact_name: libisar_android_armv7.so\n            script: build_android.sh armv7\n          - os: ubuntu-latest\n            artifact_name: libisar_android_x64.so\n            script: build_android.sh x64\n          - os: ubuntu-latest\n            artifact_name: libisar_android_x86.so\n            script: build_android.sh x86\n          - os: macos-latest\n            artifact_name: isar_ios.xcframework.zip\n            script: build_ios.sh\n          - os: ubuntu-20.04\n            artifact_name: libisar_linux_x64.so\n            script: build_linux.sh x64\n          - os: macos-latest\n            artifact_name: libisar_macos.dylib\n            script: build_macos.sh\n          - os: windows-latest\n            artifact_name: isar_windows_arm64.dll\n            script: build_windows.sh\n          - os: windows-latest\n            artifact_name: isar_windows_x64.dll\n            script: build_windows.sh x64\n    runs-on: ${{ matrix.os }}\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v4\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Set env\n        run: echo \"ISAR_VERSION=${{ github.ref_name }}\" >> $GITHUB_ENV\n      - name: Build binary\n        run: bash tool/${{ matrix.script }}\n      - name: Upload binary\n        uses: svenstaro/upload-release-action@v2\n        with:\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n          file: ${{ matrix.artifact_name }}\n          asset_name: ${{ matrix.artifact_name }}\n          tag: ${{ github.ref }}\n\n  testlab:\n    needs: build_binaries\n    uses: ./.github/workflows/testlab.yaml\n    secrets: inherit\n\n  build_inspector:\n    name: Build Inspector\n    needs: build_binaries\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Build\n        run: flutter build web --base-href /${{ github.ref_name }}/\n        working-directory: packages/isar_inspector\n      - name: Deploy to GitHub Pages\n        uses: JamesIves/github-pages-deploy-action@v4\n        with:\n          folder: packages/isar_inspector/build/web\n          repository-name: isar-community/inspector\n          token: ${{ secrets.TOKEN }}\n          target-folder: ${{ github.ref_name }}\n          clean: false\n\n  upload_to_repo:\n    needs: build_binaries\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download all artifacts\n        uses: actions/download-artifact@v2\n        with:\n          path: binaries/\n      - name: List contents of downloaded artifacts\n        run: |\n          echo \"Listing contents of all downloaded artifacts...\"\n          ls -Rlh binaries/\n          echo \"Listing complete.\"\n      - name: Setup Git and clone target repository\n        run: |\n          git config --global user.email \"vicente.russo@gmail.com\"\n          git config --global user.name \"GitHub Actions\"\n          git clone https://github.com/isar-community/binaries repo\n          cd repo\n          git checkout main || git checkout -b main\n        env:\n          GITHUB_TOKEN: ${{ secrets.DEPLOY_TOKEN }}\n      - name: Copy binaries to repository and push\n        run: |\n          cd repo\n          ISAR_VERSION=$(echo \"${{ github.ref_name }}\" | sed 's/refs\\/tags\\///')\n          echo \"Deploying binaries to version: $ISAR_VERSION\"\n          mkdir -p \"$ISAR_VERSION\"\n          cp ../binaries/**/* \"$ISAR_VERSION\"\n          git add .\n          git commit -m \"Deploy binaries for version $ISAR_VERSION\" || echo \"No changes to commit\"\n          git push https://x-access-token:${GITHUB_TOKEN}@github.com/isar-community/binaries.git main\n        env:\n          GITHUB_TOKEN: ${{ secrets.DEPLOY_TOKEN }}\n\n  # publish:\n  #   name: Publish\n  #   needs: build_inspector\n  #   runs-on: ubuntu-latest\n  #   steps:\n  #     - uses: actions/checkout@v4\n  #     - uses: subosito/flutter-action@v2\n  #     - name: pub get\n  #       run: dart pub get\n  #       working-directory: packages/isar\n  #     - name: Download Binaries\n  #       run: sh tool/download_binaries.sh\n  #     - name: pub.dev credentials\n  #       run: |\n  #         mkdir -p $HOME/.config/dart\n  #         echo '${{ secrets.PUB_JSON }}' >> $HOME/.config/dart/pub-credentials.json\n  #     - name: Publish isar\n  #       run: dart pub publish --force\n  #       working-directory: packages/isar\n  #     - name: Publish isar_generator\n  #       run: dart pub publish --force\n  #       working-directory: packages/isar_generator\n  #     - name: Publish isar_flutter_libs\n  #       run: dart pub publish --force\n  #       working-directory: packages/isar_flutter_libs\n"
  },
  {
    "path": ".github/workflows/skynet.yaml",
    "content": "name: Triggers remote jenkins\n\non:\n  push:\n    branches:\n      - main\n      - v3\n\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\njobs:\n  trigger_remote:\n    name: Trigger remote jenkins instance\n    runs-on: ubuntu-latest\n    steps:\n      - name: Invoke trigger\n        run: |\n          curl -s 'https://isar-community.dev/git/notifyCommit?url=https://github.com/isar-community/isar.git&token=a641b59c61c22effd6dd258f8e713c7b'\n"
  },
  {
    "path": ".github/workflows/test.yaml",
    "content": "name: Dart CI\n\non:\n  push:\n    branches:\n      - v3\n  pull_request:\n    branches:\n      - v3\n\njobs:\n  version:\n    name: Version Display\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - run: flutter --version\n\n  format:\n    name: Check formatting\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Check formatting\n        run: dart format --set-exit-if-changed .\n\n  lint:\n    name: Check lints\n    runs-on: ubuntu-latest\n    if: ${{ false }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - run: flutter pub get\n        working-directory: packages/isar\n      - run: flutter pub get\n        working-directory: packages/isar_flutter_libs\n      - run: flutter pub get\n        working-directory: packages/isar_generator\n      - run: flutter pub get\n        working-directory: packages/isar_inspector\n      - run: flutter pub get\n        working-directory: examples/pub\n      - run: |\n          flutter pub get\n          flutter pub run build_runner build\n          dart tool/generate_all_tests.dart\n        working-directory: packages/isar_test\n      - name: Lint\n        run: flutter analyze\n\n  test:\n    name: Dart Test\n    strategy:\n      matrix:\n        os: [macos-latest, ubuntu-latest, windows-latest]\n      fail-fast: false\n    runs-on: ${{ matrix.os }}\n    steps:\n      - run: echo \"$OSTYPE\"\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core\n        run: sh tool/build.sh\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Flutter Unit tests\n        run: flutter test -j 1\n        working-directory: packages/isar_test\n\n  valgrind:\n    name: Valgrind\n    runs-on: ubuntu-latest\n    if: ${{ false }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Install valgrind and llvm\n        run: sudo apt update && sudo apt install -y valgrind libclang-dev\n      - name: Build Isar Core\n        run: sh tool/build.sh\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Valgrind\n        run: |\n          dart compile exe integration_test/all_tests.dart\n          valgrind \\\n            --leak-check=full \\\n            --error-exitcode=1 \\\n            --show-mismatched-frees=no \\\n            --show-possibly-lost=no \\\n            --errors-for-leak-kinds=definite \\\n            integration_test/all_tests.exe\n        working-directory: packages/isar_test\n\n  coverage:\n    name: Code Coverage\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core\n        run: sh tool/build.sh\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Add packages\n        run: |\n          flutter pub add json_annotation\n          flutter pub add intl\n          flutter pub add isar_test --path ../isar_test\n        working-directory: packages/isar\n      - name: Collect isar Coverage\n        run: |\n          flutter test --coverage --coverage-path lcov_isar.info\n        working-directory: packages/isar\n      - name: Collect isar_test Coverage\n        run: |\n          flutter test --coverage ../isar_test/test --coverage-path lcov_isar_test.info\n        working-directory: packages/isar\n      - name: Upload isar Coverage\n        uses: codecov/codecov-action@v3\n        with:\n          files: packages/isar/lcov_isar.info\n      - name: Upload isar_test Coverage\n        uses: codecov/codecov-action@v3\n        with:\n          files: packages/isar/lcov_isar_test.info\n\n  test_generator:\n    name: Generator Test\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Run Generator Unit tests\n        run: |\n          dart pub get\n          dart test\n        working-directory: packages/isar_generator\n\n  integration_test_ios:\n    name: Integration Test iOS\n    runs-on: macos-12\n    steps:\n      - uses: actions/checkout@v4\n      - name: Start simulator\n        uses: futureware-tech/simulator-action@v3\n        with:\n          model: iPhone 13\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core\n        run: |\n          bash tool/build_ios.sh\n          unzip isar_ios.xcframework.zip -d packages/isar_flutter_libs/ios\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Flutter Integration tests\n        run: flutter test integration_test/integration_test.dart --dart-define STRESS=true\n        working-directory: packages/isar_test\n\n  integration_test_android:\n    name: Integration Test Android\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v3\n        with:\n          java-version: \"11\"\n          distribution: \"zulu\"\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core\n        run: |\n          bash tool/build_android.sh x64\n          mkdir -p packages/isar_flutter_libs/android/src/main/jniLibs/x86_64\n          mv libisar_android_x64.so packages/isar_flutter_libs/android/src/main/jniLibs/x86_64/libisar.so\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Flutter Integration tests\n        continue-on-error: true\n        timeout-minutes: ${{ inputs.timeout_minutes }}\n        uses: Wandalen/wretry.action@v1.0.36\n        with:\n          action: reactivecircus/android-emulator-runner@v2\n          with: |\n            api-level: 29\n            arch: x86_64\n            profile: pixel\n            working-directory: packages/isar_test\n            script: flutter test integration_test/integration_test.dart --dart-define STRESS=true\n\n  integration_test_macos:\n    name: Integration Test macOS\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          # flutter-version: \"3.3.10\" # https://github.com/flutter/flutter/issues/118469\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core\n        run: |\n          bash tool/build_macos.sh\n          install_name_tool -id @rpath/libisar.dylib libisar_macos.dylib\n          mv libisar_macos.dylib packages/isar_flutter_libs/macos/libisar.dylib\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Flutter Driver tests\n        run: |\n          flutter config --enable-macos-desktop\n          flutter test -d macos integration_test/integration_test.dart --dart-define STRESS=true\n        working-directory: packages/isar_test\n\n  integration_test_linux:\n    name: Integration Test Linux\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Install Linux requirements\n        run: sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev\n      - name: Setup headless display\n        uses: pyvista/setup-headless-display-action@v1\n      - name: Build Isar Core\n        run: |\n          bash tool/build_linux.sh x64\n          mv libisar_linux_x64.so packages/isar_flutter_libs/linux/libisar.so\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Flutter Driver tests\n        run: |\n          flutter config --enable-linux-desktop\n          flutter test -d linux integration_test/integration_test.dart --dart-define STRESS=true\n        working-directory: packages/isar_test\n\n  integration_test_windows:\n    name: Integration Test Windows\n    runs-on: windows-2019\n    if: ${{ false }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core\n        run: |\n          bash tool/build_windows.sh x64\n          mv isar_windows_x64.dll packages/isar_flutter_libs/windows/libisar.dll\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Run Flutter Driver tests\n        run: |\n          flutter config --enable-windows-desktop\n          flutter test -d windows integration_test/integration_test.dart --dart-define STRESS=true\n        working-directory: packages/isar_test\n\n  drive_chrome:\n    runs-on: ubuntu-latest\n    if: ${{ false }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Install chromedricer\n        uses: nanasess/setup-chromedriver@v1\n      - name: Prepare chromedricer\n        run: chromedriver --port=4444 &\n      - name: Run Dart tests in browser\n        run: |\n          flutter pub get\n          dart tool/generate_long_double_test.dart\n          dart tool/generate_all_tests.dart\n          flutter pub run build_runner build\n          flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart -d web-server --browser-name chrome\n        working-directory: packages/isar_test\n\n  drive_safari:\n    runs-on: macos-latest\n    if: ${{ false }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare safaridricer\n        run: |\n          sudo safaridriver --enable\n          safaridriver --port=4444 &\n      - name: Run Dart tests in browser\n        run: |\n          flutter pub get\n          dart tool/generate_long_double_test.dart\n          flutter pub run build_runner build\n          dart tool/generate_all_tests.dart\n          flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart -d web-server --browser-name safari\n        working-directory: packages/isar_test\n\n  drive_firefox:\n    runs-on: ubuntu-latest\n    if: ${{ false }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Install geckodriver\n        uses: browser-actions/setup-geckodriver@latest\n      - name: Prepare geckodriver\n        run: geckodriver --port=4444 &\n      - name: Run Dart tests in browser\n        run: |\n          flutter pub get\n          dart tool/generate_long_double_test.dart\n          flutter pub run build_runner build\n          dart tool/generate_all_tests.dart\n          flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart -d web-server --browser-name firefox\n        working-directory: packages/isar_test\n"
  },
  {
    "path": ".github/workflows/testlab.yaml",
    "content": "on: workflow_call\n\njobs:\n  firebase_testlab_android:\n    name: Firebase Testlab Android\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ vars.FLUTTER_VERSION }}\n      - name: Prepare Build\n        uses: ./.github/actions/prepare-build\n      - name: Build Isar Core arm64\n        run: |\n          bash tool/build_android.sh arm64\n          mkdir -p packages/isar_flutter_libs/android/src/main/jniLibs/arm64-v8a\n          mv libisar_android_arm64.so packages/isar_flutter_libs/android/src/main/jniLibs/arm64-v8a/libisar.so\n      - name: Build Isar Core armv7\n        run: |\n          bash tool/build_android.sh armv7\n          mkdir -p packages/isar_flutter_libs/android/src/main/jniLibs/armeabi-v7a\n          mv libisar_android_armv7.so packages/isar_flutter_libs/android/src/main/jniLibs/armeabi-v7a/libisar.so\n      - name: Prepare Tests\n        run: sh tool/prepare_tests.sh\n      - name: Build dummy APK\n        run: flutter build apk integration_test/integration_test.dart\n        working-directory: packages/isar_test\n      - name: Build APKs\n        run: |\n          ./gradlew app:assembleAndroidTest\n          ./gradlew app:assembleDebug -Ptarget=integration_test/integration_test.dart\n        working-directory: packages/isar_test/android\n      - name: Login to Google Cloud\n        uses: \"google-github-actions/auth@v1\"\n        with:\n          credentials_json: \"${{ secrets.FIREBASE_JSON }}\"\n      - name: Run tests\n        run: |\n          gcloud firebase test android run \\\n            --project isar-community \\\n            --type instrumentation \\\n            --timeout 5m \\\n            --device model=starqlteue,version=26 \\\n            --device model=cheetah,version=33 \\\n            --device model=shiba,version=34 \\\n            --app build/app/outputs/apk/debug/app-debug.apk \\\n            --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk\n        working-directory: packages/isar_test\n"
  },
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.class\n*.lock\n*.log\n.DS_Store\n.vscode/\n.idea\n\n# Dart related\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n**/generated_plugin_registrant.dart\n.packages\n.pub-cache/\n.pub/\nbuild/\n\n# Rust related\ntarget/\n*.a\n*.so\n*.dylib\n*.dll\n*.zip\n*.xcframework/\nisar-dart.h\n\n# Android related\n**/android/**/gradle-wrapper.jar\n.gradle/\n**/android/captures/\n**/android/gradlew\n**/android/gradlew.bat\n**/android/local.properties\n**/android/**/GeneratedPluginRegistrant.java\n\n# iOS/XCode related\n**/ios/**/*.mode1v3\n**/ios/**/*.mode2v3\n**/ios/**/*.moved-aside\n**/ios/**/*.pbxuser\n**/ios/**/*.perspectivev3\n**/ios/**/*sync/\n**/ios/**/.sconsign.dblite\n**/ios/**/.tags*\n**/ios/**/.vagrant/\n**/ios/**/DerivedData/\n**/ios/**/Icon?\n**/ios/**/Pods/\n**/ios/**/.symlinks/\n**/ios/**/profile\n**/ios/**/xcuserdata\n**/ios/.generated/\n**/ios/Flutter/.last_build_id\n**/ios/Flutter/App.framework\n**/ios/Flutter/Flutter.framework\n**/ios/Flutter/Flutter.podspec\n**/ios/Flutter/Generated.xcconfig\n**/ios/Flutter/ephemeral\n**/ios/Flutter/app.flx\n**/ios/Flutter/app.zip\n**/ios/Flutter/flutter_assets/\n**/ios/Flutter/flutter_export_environment.sh\n**/ios/ServiceDefinitions.json\n**/ios/Runner/GeneratedPluginRegistrant.*\n\n# macOS\n**/macos/Flutter/GeneratedPluginRegistrant.swift\n**/ephemeral\n**/.plugin_symlinks/\n\n# Coverage\ncoverage/\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"packages/isar_core\",\n    \"packages/isar_core_ffi\",\n    \"packages/mdbx_sys\"\n]\n\n[profile.release]\nlto = true\ncodegen-units = 1\npanic = \"abort\"\nstrip = \"symbols\""
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "TODO.md",
    "content": "<h1 align=\"center\"> Roadmap and TODOs</p>\n\n\n# Documentation\n\n## API Docs\n\n- [ ] Document all public APIs\n\n## Schema\n\n- [x] Update schema migration instructions\n- [ ] Document all annotation options\n\n## CRUD\n\n- [ ] Document sync operations\n- [x] `getAll()`, `putAll`, `deleteAll()`\n- [ ] `getBy...()`, `deleteBy...()`\n\n## Queries\n\n- [x] Filter groups\n- [x] Boolean operators `and()`, `or()`, `not()`\n- [x] Offset, limit\n- [x] Distinct where clauses\n- [x] Different filter operations (`equalTo`, `beginsWith()` etc.)\n- [ ] Better explanation for distinct and sorted where clauses\n- [ ] Watching queries\n\n## Indexes\n\n- [ ] Intro\n- [x] What are they\n- [ ] Why use them\n- [x] How to in isar?\n\n## Examples\n\n- [ ] Create minimal example\n- [ ] Create complex example with indexes, filter groups etc.\n- [ ] More Sample Apps\n\n## Tutorials\n\n- [ ] How to write fast queries\n- [ ] Build a simple offline first app\n- [ ] Advanced queries\n\n\n----\n\n\n# Isar Dart\n\n## Features\n\n- [x] Distinct by\n- [x] Offset, Limit\n- [x] Sorted by\n\n## Fixes\n\n- [x] Provide an option to change collection accessor names\n\n## Unit tests\n\n- [x] Download binaries automatically for tests\n\n### Queries\n\n- [x] Restructure query tests to make them less verbose\n- [x] Define models that can be reused across tests\n- [x] Where clauses with string indexes (value, hash, words, case-sensitive)\n- [x] Distinct where clauses\n- [x] String filter operations\n\n\n----\n\n\n# Isar Core\n\n## Features (low priority)\n\n- [ ] Draft Synchronization\n- [x] Relationships\n\n## Unit tests\n\n- [ ] Make mdbx unit tests bulletproof\n- [x] Migration tests\n- [x] Binary format\n- [x] CRUD\n- [x] Links\n- [ ] QueryBuilder\n- [ ] WhereClause\n- [ ] WhereExecutor\n- [x] CollectionMigrator\n- [ ] Watchers\n\n\n----\n\n\n# Isar Web\n\n- [ ] MVP\n\n\n"
  },
  {
    "path": "docs/.gitignore",
    "content": ".DS_Store\n\nnode_modules\n.temp\n.cache\ndist\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Isar Docs\n\nRun the docs locally:\n\n```\nnpm install\n\nnpm run dev\n```\n\n## Create a new language\n\n1. Create a new folder in `docs` with the language code (e.g. `de` for German).\n2. Add the locale config to `.vueepress/locales.ts`.\n3. Start translating the existing pages."
  },
  {
    "path": "docs/docs/.vuepress/config.ts",
    "content": "import { shikiPlugin } from '@vuepress/plugin-shiki'\nimport { DefaultThemeLocaleData, defineUserConfig, LocaleConfig, SiteLocaleConfig, } from 'vuepress'\nimport { defaultTheme } from 'vuepress'\nimport { viteBundler } from 'vuepress'\nimport { getLocalePath, locales } from './locales'\nimport * as path from 'path'\nimport * as fs from 'fs'\n\nconst vueLocales: SiteLocaleConfig = {}\nfor (const locale of locales) {\n    vueLocales[getLocalePath(locale.code)] = {\n        lang: locale.language,\n        title: locale.dbName,\n        description: locale.dbDescription,\n    }\n}\n\nconst themeLocales: LocaleConfig<DefaultThemeLocaleData> = {}\nfor (const locale of locales) {\n    themeLocales[getLocalePath(locale.code)] = {\n        selectLanguageName: locale.language,\n        selectLanguageText: locale.selectLanguage,\n        editLinkText: locale.editPage,\n        lastUpdatedText: locale.lastUpdated,\n        contributorsText: locale.contributors,\n        tip: locale.tip,\n        warning: locale.warning,\n        danger: locale.danger,\n        notFound: locale.notFound,\n        backToHome: locale.backToHome,\n        sidebar: getSidebar({\n            locale: locale.code,\n            tutorials: locale.tutorials,\n            concepts: locale.concepts,\n            recipes: locale.recipes,\n            sampleApps: locale.sampleApps,\n            chnagelog: locale.changelog,\n            contributors: locale.contributors,\n        }),\n    }\n}\n\nexport default defineUserConfig({\n    locales: vueLocales,\n    bundler: viteBundler({}),\n    base: '/v3/',\n    theme: defaultTheme({\n        logo: \"/isar.svg\",\n        repo: \"isar-community/isar\",\n        docsRepo: \"isar-community/isar\",\n        docsDir: \"docs/docs\",\n        contributors: true,\n        locales: themeLocales,\n        navbar: [\n            {\n                text: \"pub.dev\",\n                link: \"https://pub.dev/packages/isar\",\n            },\n            {\n                text: \"API\",\n                link: \"https://pub.dev/documentation/isar/latest/isar/isar-library.html\",\n            },\n            {\n                text: \"Telegram\",\n                link: \"https://t.me/isardb\",\n            },\n            {\n                text: \"v3.x\",\n                children: [\n                    {\n                        text: \"v4.x\",\n                        link: \"https://isar-community.dev\",\n                    },\n                    {\n                        text: \"v3.x\",\n                        link: \"https://isar-community.dev/v3\",\n                    },\n                ],\n            },\n        ],\n        sidebarDepth: 1,\n\n    }),\n    markdown: {\n        code: {\n            lineNumbers: false,\n        },\n    },\n    plugins: [\n        [\n            shikiPlugin({\n                theme: \"one-dark-pro\",\n            }),\n            {\n                name: 'redirect-locale',\n                clientConfigFile: path.resolve(__dirname, 'redirect.ts'),\n            },\n        ],\n    ],\n    head: [\n        [\n            \"link\",\n            {\n                rel: \"icon\",\n                type: \"image/png\",\n                sizes: \"256x256\",\n                href: `/icon-256x256.png`,\n            },\n        ],\n        [\n            \"link\",\n            {\n                rel: \"icon\",\n                type: \"image/png\",\n                sizes: \"512x512\",\n                href: `/icon-512x512.png`,\n            },\n        ],\n        [\n            \"link\",\n            {\n                rel: \"stylesheet\",\n                href: \"https://fonts.googleapis.com/css2?family=Montserrat:wght@800&display=swap\",\n            },\n        ],\n        [\"meta\", { name: \"application-name\", content: \"Isar Database\" }],\n        [\"meta\", { name: \"apple-mobile-web-app-title\", content: \"Isar Database\" }],\n        [\n            \"meta\",\n            { name: \"apple-mobile-web-app-status-bar-style\", content: \"black\" },\n        ],\n        [\n            \"script\",\n            {\n                async: \"\",\n                src: \"https://www.googletagmanager.com/gtag/js?id=G-36LNDL9RHB\",\n            },\n        ],\n        [\n            \"script\",\n            {},\n            `window.dataLayer = window.dataLayer || [];\n                function gtag(){dataLayer.push(arguments);}\n                gtag('js', new Date());\n                gtag('config', 'G-36LNDL9RHB');`,\n        ],\n        [\n            \"script\",\n            {},\n            `(function(c,l,a,r,i,t,y){\n            c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};\n            t=l.createElement(r);t.async=1;t.src=\"https://www.clarity.ms/tag/\"+i;\n            y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);\n          })(window, document, \"clarity\", \"script\", \"lkyzg3xacc\");`,\n        ],\n    ],\n})\n\nfunction getSidebar({ locale, tutorials, concepts, recipes, sampleApps, chnagelog, contributors }) {\n    return [\n        {\n            text: tutorials,\n            children: getSidebarChildren(locale, [\"tutorials/quickstart.md\"])\n        },\n        {\n            text: concepts,\n            children: getSidebarChildren(\n                locale,\n                [\n                    \"schema.md\",\n                    \"crud.md\",\n                    \"queries.md\",\n                    \"transactions.md\",\n                    \"indexes.md\",\n                    \"links.md\",\n                    \"watchers.md\",\n                    \"limitations.md\",\n                    \"faq.md\",\n                ],\n            ),\n        },\n        {\n            text: recipes,\n            children: getSidebarChildren(\n                locale,\n                [\n                    \"recipes/full_text_search.md\",\n                    \"recipes/multi_isolate.md\",\n                    \"recipes/string_ids.md\",\n                    \"recipes/data_migration.md\",\n                ]\n            ),\n        },\n        {\n            text: sampleApps,\n            link: \"https://github.com/isar-community/isar/tree/main/examples\",\n        },\n        {\n            text: chnagelog,\n            link: \"https://github.com/isar-community/isar/blob/main/packages/isar/CHANGELOG.md\",\n        },\n        {\n            text: contributors,\n            link: \"https://github.com/isar-community/isar#contributors-\",\n        },\n    ]\n}\n\nfunction getSidebarChildren(locale: string, children: string[]) {\n    const localePath = getLocalePath(locale)\n    return children.map((child) => {\n        if (locale === \"en\") {\n            return '/' + child\n        }\n        const file = path.resolve(__dirname, '../', localePath.substring(1), child)\n        if (fs.existsSync(file)) {\n            return localePath + child\n        } else {\n            return '/' + child\n        }\n    });\n}"
  },
  {
    "path": "docs/docs/.vuepress/locales.ts",
    "content": "export interface LocalConfig {\n  code: string;\n  language: string;\n  selectLanguage: string;\n  editPage: string;\n  lastUpdated: string;\n  tip: string;\n  warning: string;\n  danger: string;\n  notFound: string[];\n  backToHome: string;\n  translationOutdated: string;\n\n  dbName: string;\n  dbDescription: string;\n\n  tutorials: string;\n  concepts: string;\n  recipes: string;\n  sampleApps: string;\n  changelog: string;\n  contributors: string;\n}\n\nexport function getLocalePath(code: string): string {\n  if (code === \"en\") {\n    return \"/\";\n  } else {\n    return \"/\" + code + \"/\";\n  }\n}\n\nexport const locales: LocalConfig[] = [\n  {\n    code: \"en\",\n    language: \"English\",\n    selectLanguage: \"Select Language\",\n    editPage: \"Edit Page\",\n    lastUpdated: \"Last Updated\",\n    tip: \"Tip\",\n    warning: \"Warning\",\n    danger: \"Danger\",\n    notFound: [\n      \"Nothing to see here.\",\n      \"How did we end up here?\",\n      \"This is a four-oh-four...\",\n      \"Looks like we have a broken link.\",\n    ],\n    backToHome: \"Back to Home\",\n    translationOutdated: \"Translation is outdated. Please help us update it!\",\n    dbName: \"Isar Database\",\n    dbDescription: \"Super Fast Cross-Platform Database for Flutter\",\n    tutorials: \"TUTORIALS\",\n    concepts: \"CONCEPTS\",\n    recipes: \"RECIPES\",\n    sampleApps: \"Sample Apps\",\n    changelog: \"Changelog\",\n    contributors: \"Contributors\",\n  },\n  {\n    code: \"de\",\n    language: \"Deutsch\",\n    selectLanguage: \"Sprache wählen\",\n    editPage: \"Seite bearbeiten\",\n    lastUpdated: \"Zuletzt aktualisiert\",\n    tip: \"Tipp\",\n    warning: \"Warnung\",\n    danger: \"Achtung\",\n    notFound: [\n      \"Hier gibt es nichts zu sehen.\",\n      \"Wie sind wir hier gelandet?\",\n      \"Das ist ein vier-null-vier...\",\n      \"Sieht aus als hätten wir einen kaputten Link.\",\n    ],\n    backToHome: \"Zurück zur Startseite\",\n    translationOutdated:\n      \"Übersetzung ist veraltet. Bitte hilf uns, sie zu aktualisieren!\",\n    dbName: \"Isar Datenbank\",\n    dbDescription: \"Super Schnelle Cross-Platform Flutter Datenbank\",\n    tutorials: \"TUTORIALS\",\n    concepts: \"KONZEPTE\",\n    recipes: \"REZEPTE\",\n    sampleApps: \"Beispiel Apps\",\n    changelog: \"Änderungsprotokoll\",\n    contributors: \"Mitwirkende\",\n  },\n  {\n    code: \"ja\",\n    language: \"日本語\",\n    selectLanguage: \"言語の選択\",\n    editPage: \"編集ページ\",\n    lastUpdated: \"最終更新日\",\n    tip: \"ヒント\",\n    warning: \"警告\",\n    danger: \"危険\",\n    notFound: [\n      \"何も見つかりませんでした.\",\n      \"どうしてこんなところに辿り着いたのだろう...\",\n      \"ここは404ページのようです...\",\n      \"リンク切れのようです。\",\n    ],\n    backToHome: \"ホームに戻る\",\n    translationOutdated:\n      \"翻訳は古くなっています。翻訳の更新にご協力頂けませんか？\",\n    dbName: \"Isar Database\",\n    dbDescription: \"Flutterのための超高速クロスプラットフォームDatabase\",\n    tutorials: \"チュートリアル\",\n    concepts: \"コンセプト\",\n    recipes: \"レシピ集\",\n    sampleApps: \"サンプルアプリ\",\n    changelog: \"変更履歴\",\n    contributors: \"貢献者の方々\",\n  },\n  {\n    code: \"ko\",\n    language: \"한국어\",\n    selectLanguage: \"언어 선택\",\n    editPage: \"페이지 편집\",\n    lastUpdated: \"마지막 업데이트\",\n    tip: \"팁\",\n    warning: \"경고\",\n    danger: \"위험\",\n    notFound: [\n      \"여기는 볼 것이 없다.\",\n      \"우리가 어떻게 여기까지 오게 되었나요?\",\n      \"여기는 404...\",\n      \"연결이 끊어진 것 같습니다.\",\n    ],\n    backToHome: \"홈으로 돌아가기\",\n    translationOutdated: \"번역이 낡았습니다. 업데이트 도와주세요!\",\n    dbName: \"Isar 데이터베이스\",\n    dbDescription: \"플러터를 위한 초고속 크로스 플랫폼 데이터베이스\",\n    tutorials: \"튜토리얼\",\n    concepts: \"개념\",\n    recipes: \"레시피\",\n    sampleApps: \"샘플 앱\",\n    changelog: \"체인지로그\",\n    contributors: \"기여자들\",\n  },\n  {\n    code: \"es\",\n    language: \"Español\",\n    selectLanguage: \"Seleccionar Idioma\",\n    editPage: \"Editar Página\",\n    lastUpdated: \"Última actualización\",\n    tip: \"Consejo\",\n    warning: \"Advertencia\",\n    danger: \"Peligro\",\n    notFound: [\n      \"No hay nada para ver aquí.\",\n      \"Cómo llegamos aquí?\",\n      \"Esto es vergonzoso, no tenemos nada...\",\n      \"Parece que hay un enlace roto.\",\n    ],\n    backToHome: \"Volver al inicio\",\n    translationOutdated:\n      \"Esta traducción está desactualizada. Por favor ayúdanos a mantenerla al día!\",\n    dbName: \"Isar Database\",\n    dbDescription: \"Base de Datos Super rápida, Multiplataforma para Flutter\",\n    tutorials: \"TUTORIALES\",\n    concepts: \"CONCEPTOS\",\n    recipes: \"RECETAS\",\n    sampleApps: \"Aplicaciones de Ejemplo\",\n    changelog: \"Registro de cambios\",\n    contributors: \"Colaboradores\",\n  },\n  {\n    code: \"it\",\n    language: \"Italiano\",\n    selectLanguage: \"Seleziona Lingua\",\n    editPage: \"Modifica Pagina\",\n    lastUpdated: \"Ultimo aggiornamento\",\n    tip: \"Suggerimento\",\n    warning: \"Attenzione\",\n    danger: \"Pericolo\",\n    notFound: [\n      \"Nulla da vedere qui.\",\n      \"Come ci siamo finiti qui?\",\n      \"Questa è una quattro-zero-quattro...\",\n      \"Sembra che abbiamo un collegamento rotto.\",\n    ],\n    backToHome: \"Indietro alla Home\",\n    translationOutdated:\n      \"La traduzione è obsoleta. Per favore aiutaci ad aggiornarla!\",\n    dbName: \"Isar Database\",\n    dbDescription: \"Database multipiattaforma super veloce per Flutter\",\n    tutorials: \"TUTORIALS\",\n    concepts: \"CONCETTI\",\n    recipes: \"RICETTE\",\n    sampleApps: \"App d'esempio\",\n    changelog: \"Registro delle modifiche\",\n    contributors: \"Contributori\",\n  },\n  {\n    code: \"pt\",\n    language: \"Português\",\n    selectLanguage: \"Selecione o idioma\",\n    editPage: \"Editar página\",\n    lastUpdated: \"Ultima atualização\",\n    tip: \"Dica\",\n    warning: \"Aviso\",\n    danger: \"Perigo\",\n    notFound: [\n      \"Nada para ver aqui.\",\n      \"Como chegamos aqui?\",\n      \"Isso é embaraçoso, não temos nada...\",\n      \"Parece que temos um link inválido.\",\n    ],\n    backToHome: \"Voltar para Início\",\n    translationOutdated:\n      \"A tradução está desatualizada. Por favor, ajude-nos a atualizá-lo!\",\n    dbName: \"Isar Database\",\n    dbDescription: \"Banco de dados multiplataforma super rápido para Flutter\",\n    tutorials: \"TUTORIAIS\",\n    concepts: \"CONCEITOS\",\n    recipes: \"RECEITAS\",\n    sampleApps: \"Aplicativos de amostra\",\n    changelog: \"Registro de alterações\",\n    contributors: \"Contribuidores\",\n  },\n  {\n    code: \"ur\",\n    language: \"اردو\",\n    selectLanguage: \"زبان منتخب کریں\",\n    editPage: \"صفحہ میں ترمیم کریں\",\n    lastUpdated: \"آخری تازہ کاری\",\n    tip: \"ٹپ\",\n    warning: \"انتباہ\",\n    danger: \"خطرہ\",\n    notFound: [\n      \"یہاں دیکھنے کے لیے کچھ نہیں ہے۔\",\n      \"ہم یہاں کیسے پہنچے؟\",\n      \" یہ چار اوہ چار ہے۔۔۔\",\n      \"لگتا ہے ہمارے پاس کوئی ٹوٹا ہوا لنک ہے۔\",\n    ],\n    backToHome: \"گھر پر واپس\",\n    translationOutdated:\n      \"ترجمہ پرانا ہے۔ براہ کرم اسے تروتازہ کرنے میں ہماری مدد کریں!\",\n    dbName: \"Isar Database\",\n    dbDescription: \"  ڈیٹا بیس کے لیے سپر فاسٹ کراس پلیٹ فارم Flutter\",\n    tutorials: \"اسباق\",\n    concepts: \"تصورات\",\n    recipes: \"تراکیب\",\n    sampleApps: \"نمونہ ایپس\",\n    changelog: \"چینج لاگ\",\n    contributors: \"شراکت دار\",\n  },\n  {\n    code: \"fr\",\n    language: \"Français\",\n    selectLanguage: \"Sélectionner la langue\",\n    editPage: \"Modifier la page\",\n    lastUpdated: \"Dernière modification\",\n    tip: \"Conseil\",\n    warning: \"Avertissement\",\n    danger: \"Danger\",\n    notFound: [\n      'Il n\"y a rien a voir ici.',\n      \"Comment en sommes-nous arrivés là ?\",\n      \"Ceci est un quatre-cent-quatre...\",\n      \"Il semble que nous avons un lien brisé.\",\n    ],\n    backToHome: \"Retour à l'acceuil\",\n    translationOutdated: \"Translation is outdated. Please help us update it!\",\n    dbName: \"Base de données Isar\",\n    dbDescription: \"Base de données multiplateforme super rapide pour Flutter\",\n    tutorials: \"TUTORIELS\",\n    concepts: \"CONCEPTS\",\n    recipes: \"RECETTES\",\n    sampleApps: \"Exemples d'applications\",\n    changelog: \"Changements\",\n    contributors: \"Contributeurs\",\n  },\n  {\n    code: \"zh\",\n    language: \"简体中文\",\n    selectLanguage: \"选择语言\",\n    editPage: \"编辑页面\",\n    lastUpdated: \"更新日期\",\n    tip: \"提示\",\n    warning: \"警告\",\n    danger: \"危险\",\n    notFound: [\n      \"这里什么都没有。\",\n      \"怎么会来到这个页面？\",\n      \"404...\",\n      \"看起来链接失效了。\",\n    ],\n    backToHome: \"回到主页\",\n    translationOutdated: \"翻译已过期，请帮助我们更新。\",\n    dbName: \"Isar 数据库\",\n    dbDescription: \"专门为 Flutter 打造的超高速跨平台数据库\",\n    tutorials: \"教程\",\n    concepts: \"概念\",\n    recipes: \"专题\",\n    sampleApps: \"示例 App\",\n    changelog: \"更新记录\",\n    contributors: \"贡献者\",\n  },\n];\n"
  },
  {
    "path": "docs/docs/.vuepress/redirect.ts",
    "content": "import { defineClientConfig } from '@vuepress/client'\nimport { locales } from './locales'\n\nexport default defineClientConfig({\n    enhance({ app, router, siteData }) {\n        router.beforeEach((to, from) => {\n            // open vuepress for the first time\n            let isFirstStart = to.fullPath == from.fullPath\n\n            // Whether the home page is about to be displayed\n            let isHome = to.fullPath == \"/\"\n\n            if (typeof navigator != 'undefined' && isFirstStart && isHome) {\n                const lang = navigator.language.split(\"-\")[0].toLowerCase()\n\n                if (lang != \"en\" && locales.some((l) => l.code === lang)) {\n                    const redirectUrl = \"/\" + lang + \"/\"\n                    // Avoid infinite redirection\n                    if (to.fullPath != redirectUrl) {\n                        return redirectUrl\n                    }\n                }\n            }\n        })\n    }\n})"
  },
  {
    "path": "docs/docs/.vuepress/styles/index.scss",
    "content": ":root {\n  --c-brand: #4799fc;\n  --c-brand-light: #67abfd;\n\n  --c-text: rgb(30, 30, 30); // normal text\n  --c-text-light: rgb(30, 30, 30);\n  --c-text-lighter: rgb(30, 30, 30); // code block text\n  --c-text-lightest: rgba(30, 30, 30, 0.7);\n\n  .custom-container.tip {\n    color: rgb(57, 146, 255) !important;\n    border-color: rgb(57, 146, 255) !important;\n    background-color: rgba(57, 146, 255, 0.1) !important;\n  }\n\n  .custom-container.warning {\n    color: rgb(39, 31, 6) !important;\n    border-color: rgb(39, 31, 6) !important;\n    background-color: rgba(131, 122, 11, 0.15) !important;\n  }\n\n  .custom-container.danger {\n    color: rgb(170, 37, 58) !important;\n    border-color: rgb(170, 37, 58) !important;\n    background-color: rgba(170, 37, 58, 0.1) !important;\n  }\n}\n\nhtml.dark {\n  --c-brand: #67abfd;\n  --c-brand-light: #4799fc;\n\n  --c-bg: rgb(18, 18, 18);\n  --c-bg-light: rgb(30, 30, 30); // code block background\n  --code-bg-color: #1e1e1e; // code background\n\n  --c-text: rgb(183, 188, 190); // normal text\n  --c-text-light: rgba(183, 188, 190);\n  --c-text-lighter: rgb(183, 188, 190); // code block text\n  --c-text-lightest: rgba(183, 188, 190, 0.7);\n\n  --c-border: rgb(45, 45, 45);\n  --c-border-dark: rgb(60, 60, 60);\n\n  .custom-container.tip {\n    color: rgb(57, 146, 255) !important;\n    border-color: rgb(57, 146, 255) !important;\n    background-color: rgba(57, 146, 255, 0.1) !important;\n  }\n\n  .custom-container.warning {\n    color: rgb(248, 239, 159) !important;\n    border-color: rgb(248, 239, 159) !important;\n    background-color: rgba(131, 122, 11, 0.15) !important;\n  }\n\n  .custom-container.danger {\n    color: rgb(240, 158, 183) !important;\n    border-color: rgb(240, 158, 183) !important;\n    background-color: rgba(240, 158, 183, 0.1) !important;\n  }\n}\n\nh1 {\n  font-family: \"Montserrat\", sans-serif;\n\n  font-size: 38px;\n  @media (min-width: 750px) {\n    font-size: 55px;\n  }\n}\n\nh2 {\n  font-family: \"Montserrat\", sans-serif;\n  font-size: 27px;\n  @media (min-width: 750px) {\n    font-size: 38px;\n  }\n}\n\nh3 {\n  font-family: \"Montserrat\", sans-serif;\n  font-size: 20px;\n  @media (min-width: 750px) {\n    font-size: 27px;\n  }\n}\n\nh4 {\n  font-family: \"Montserrat\", sans-serif;\n  font-size: 16px;\n}\n\nmark {\n  padding: 2px;\n}\n\n.action-button.primary {\n  font-weight: 800;\n}\n\nsummary {\n  cursor: pointer;\n}\n\ndetails[open] summary {\n  margin-bottom: 0.5rem;\n}\n\n.custom-container {\n  border-radius: 10px;\n  border-left-width: 0.25rem !important;\n  border-left-style: solid;\n  border-right-style: solid;\n  border-right-width: 0.25rem;\n}\n\n.custom-container-title {\n  display: none;\n}\n\n.video-block {\n  position: relative; \n  padding-bottom: 56.25%; /* 16:9 */\n  height: 0;\n  overflow: hidden;\n  width: 100%; height: auto;\n}\n\n.video-block iframe {\n  position: absolute;\n  top: 0; left: 0;\n  width: 100%; height: 100%;\n}"
  },
  {
    "path": "docs/docs/README.md",
    "content": "---\nhome: true\ntitle: Home\nheroImage: /isar.svg\nactions:\n  - text: Let's Get Started!\n    link: /tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Made for Flutter\n    details: Minimal setup, Easy to use, no config, no boilerplate. Just add a few lines of code to get started.\n  - title: 🚀 Highly scalable\n    details: Store hundreds of thousands of records in a single NoSQL database and query them efficiently and asynchronously.\n  - title: 🍭 Feature-rich\n    details: Isar has a rich set of features to help you manage your data. Composite & multi-entry indexes, query modifiers, JSON support, and more.\n  - title: 🔎 Full-text search\n    details: Isar has built-in full-text search. Create a multi-entry index and search for records easily.\n  - title: 🧪 ACID semantics\n    details: Isar is ACID compliant and handles transactions automatically. It rolls back changes if an error occurs.\n  - title: 💃 Static typing\n    details: Isar's queries are statically typed and compile-time checked. No need to worry about runtime errors. \n  - title: 📱 Multiplatform\n    details: iOS, Android, Desktop, and FULL WEB SUPPORT!\n  - title: ⏱ Asynchronous\n    details: Parallel query operations & multi-isolate support out-of-the-box\n  - title: 🦄 Open Source\n    details: Everything is open source and free forever!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/crud.md",
    "content": "---\ntitle: Create, Read, Update, Delete\n---\n\n# Create, Read, Update, Delete\n\nWhen you have your collections defined, learn how to manipulate them!\n\n## Opening Isar\n\nBefore you can do anything, we need an Isar instance. Each instance requires a directory with write permission where the database file can be stored. If you don't specify a directory, Isar will find a suitable default directory for the current platform.\n\nProvide all the schemas you want to use with the Isar instance. If you open multiple instances, you still have to provide the same schemas to each instance.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [RecipeSchema],\n  directory: dir.path,\n);\n```\n\nYou can use the default config or provide some of the following parameters:\n\n| Config              | Description                                                                                                                                                                                                                                                                                  |\n| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `name`              | Open multiple instances with distinct names. By default, `\"default\"` is used.                                                                                                                                                                                                                |\n| `directory`         | The storage location for this instance. Not required for web.                                                                                                                                                                                                                                |\n| `maxSizeMib`        | The maximum size of the database file in MiB. Isar uses virtual memory which is not an endless resource so be mindful with the value here. If you open multiple instances they share the available virtual memory so each instance should have a smaller `maxSizeMib` . The default is 2048. |\n| `relaxedDurability` | Relaxes the durability guarantee to increase write performance. In case of a system crash (not app crash), it is possible to lose the last committed transaction. Corruption is not possible                                                                                                 |\n| `compactOnLaunch`   | Conditions to check whether the database should be compacted when the instance is opened.                                                                                                                                                                                                    |\n| `inspector`         | Enabled the Inspector for debug builds. For profile and release builds this option is ignored.                                                                                                                                                                                               |\n\nIf an instance is already open, calling `Isar.open()` will yield the existing instance regardless of the specified parameters. That's useful for using Isar in an isolate.\n\n:::tip\nConsider using the [path_provider](https://pub.dev/packages/path_provider) package to get a valid path on all platforms.\n:::\n\nThe storage location of the database file is `directory/name.isar`\n\n## Reading from the database\n\nUse `IsarCollection` instances to find, query, and create new objects of a given type in Isar.\n\nFor the examples below, we assume that we have a collection `Recipe` defined as follows:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### Get a collection\n\nAll your collections live in the Isar instance. You can get the recipes collection with:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\nThat was easy! If you don't want to use collection accessors, you can also use the `collection()` method:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### Get an object (by id)\n\nWe don't have data in the collection yet but let's pretend we do so we can get an imaginary object by the id `123`\n\n```dart\nfinal recipe = await isar.recipes.get(123);\n```\n\n`get()` returns a `Future` with either the object or `null` if it does not exist. All Isar operations are asynchronous by default, and most of them have a synchronous counterpart:\n\n```dart\nfinal recipe = isar.recipes.getSync(123);\n```\n\n:::warning\nYou should default to the asynchronous version of methods in your UI isolate. Since Isar is very fast, it is often acceptable to use the synchronous version.\n:::\n\nIf you want to get multiple objects at once, use `getAll()` or `getAllSync()`:\n\n```dart\nfinal recipe = await isar.recipes.getAll([1, 2]);\n```\n\n### Query objects\n\nInstead of getting objects by id you can also query a list of objects matching certain conditions using `.where()` and `.filter()`:\n\n```dart\nfinal allRecipes = await isar.recipes.where().findAll();\n\nfinal favorites = await isar.recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ Learn more: [Queries](queries)\n\n## Modifying the database\n\nIt's finally time to modify our collection! To create, update, or delete objects, use the respective operations wrapped in a write transaction:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await isar.recipes.get(123)\n\n  recipe.isFavorite = false;\n  await isar.recipes.put(recipe); // perform update operations\n\n  await isar.recipes.delete(123); // or delete operations\n});\n```\n\n➡️ Learn more: [Transactions](transactions)\n\n### Insert object\n\nTo persist an object in Isar, insert it into a collection. Isar's `put()` method will either insert or update the object depending on whether it already exists in the collection.\n\nIf the id field is `null` or `Isar.autoIncrement`, Isar will use an auto-increment id.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await isar.recipes.put(pancakes);\n})\n```\n\nIsar will automatically assign the id to the object if the `id` field is non-final.\n\nInserting multiple objects at once is just as easy:\n\n```dart\nawait isar.writeTxn(() async {\n  await isar.recipes.putAll([pancakes, pizza]);\n})\n```\n\n### Update object\n\nBoth creating and updating works with `collection.put(object)`. If the id is `null` (or does not exist), the object is inserted; otherwise, it is updated.\n\nSo if we want to unfavorite our pancakes, we can do the following:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await isar.recipes.put(pancakes);\n});\n```\n\n### Delete object\n\nWant to get rid of an object in Isar? Use `collection.delete(id)`. The delete method returns whether an object with the specified id was found and deleted. If you want to delete the object with id `123`, for example, you can do:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await isar.recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nSimilarly to get and put, there is also a bulk delete operation that returns the number of deleted objects:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\nIf you don't know the ids of the objects you want to delete, you can use a query:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/de/README.md",
    "content": "---\nhome: true\ntitle: Home\nheroImage: /isar.svg\nactions:\n  - text: Auf Los geht's los!\n    link: /de/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Für Flutter gemacht\n    details: Minimales Setup, einfach zu bedienen, keine Konfiguration, kein Boilerplate. Mit ein paar Zeilen Code geht's los.\n  - title: 🚀 Skalierbar\n    details: Speichere Hunderttausende von Datensätzen und rufe sie effizient und asynchron ab.\n  - title: 🍭 Viele Features\n    details: Isar hat unzählige Features. Komposit- und Mehrfach-Indizes, Query-Modifikatoren, JSON und mehr.\n  - title: 🔎 Volltextsuche\n    details: Volltextsuche ist integriert. Erstelle einen Mehrfach-Index und suche nach Datensätzen.\n  - title: 🧪 ACID Semantik\n    details: Isar ist ACID-konform und verwaltet Transaktionen automatisch. Änderungen werden rückgängig gemacht, falls ein Fehler auftritt.\n  - title: 💃 Statische Typisierung\n    details: Abfragen sind statisch typisiert und werden zur Kompilierzeit überprüft. Laufzeitfehler sind ein Problem von gestern.\n  - title: 📱 Multiplatform\n    details: iOS, Android, Desktop und VOLLE WEB UNTERSTÜTZUNG!\n  - title: ⏱ Asynchron\n    details: Parallelle Abfragen und Multi-Isolate-Unterstützung.\n  - title: 🦄 Open Source\n    details: Komplett Open Source und für immer kostenlos!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/de/crud.md",
    "content": "---\ntitle: Erstellen, Lesen, Aktualisieren und Löschen\n---\n\n# Erstellen, Lesen, Aktualisieren und Löschen\n\nLerne wie du Collections in Isar nutzt nachdem du sie definiert hast.\n\n## Öffnen von Isar\n\nAls Erstes benötigen wir eine Isar Instanz. Jede Instanz erfordert einen Ordner mit Schreibrechten, in dem die Datenbankdatei gespeichert werden kann. Wenn du keinen Ordner angibst, wird Isar einen geeigneten Standardordner für die aktuelle Plattform finden.\n\nGib alle Schemas an, die du mit der Isar-Instanz verwenden möchtest. Wenn du mehrere Instanzen öffnest, musst du trotzdem jeder Instanz die gleichen Schemas mitgeben.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [RecipeSchema],\n  directory: dir.path,\n);\n```\n\nDu kannst die Standardkonfiguration verwenden oder einige der folgenden Parameter setzen:\n\n| Konfiguration       | Beschreibung                                                                                                                                                                                                        |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `name`              | Öffne mehrere Instanzen mit unterschiedlichen Namen. Standardmäßig wird `\"default\"` verwendet.                                                                                                                      |\n| `directory`         | Der Speicherort für diese Instanz. Standardmäßig wird `NSDocumentDirectory` für iOS und `getDataDirectory` für Android verwendet. Nicht erforderlich für Web.                                                       |\n| `relaxedDurability` | Entspannt die durability-Garantie, um die Schreibleistung zu erhöhen. Im Falle eines Systemabsturzes (nicht App-Absturz) ist es möglich, die letzte Transaktion zu verlieren. Datenbankkorruption ist nicht möglich |\n\nWenn eine Instanz bereits geöffnet ist, wird `Isar.open()` die vorhandene Instanz unabhängig von den angegebenen Parametern zurückgeben. Das ist nützlich, um Isar in einem Isolate zu verwenden.\n\n:::tip\nVerwende das [path_provider](https://pub.dev/packages/path_provider)-Paket, um einen gültigen Pfad auf allen Plattformen zu erhalten.\n:::\n\nDer Speicherort der Datenbankdatei ist `directory/name.isar`\n\n## Aus der Datenbank lesen\n\nVerwende `IsarCollection`-Instanzen um Objekte eines bestimmten Typs in Isar zu finden, abzufragen und neu zu erstellen.\n\nDen folgenden Beispielen liegt die Collection `Recipe` zugrunde, die wie folgt definiert ist:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### Eine Collection erhalten\n\nAlle deine Collections befinden sich in der Isar Instanz. Erhalte die Recipes-Collection über den Accessor:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\nDas war einfach! Wenn du keine Collection-Accessors verwenden möchtest, ist alternativ die `collection()`-Methode verfügbar:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### Objekt abrufen (per ID)\n\nWir haben noch keine Daten in der Collection, aber wir nehmen an, dass bereits ein Objekt mit der ID `123` existiert.\n\n```dart\nfinal recipe = await recipes.get(123);\n```\n\nDie `get()`-Methode gibt ein `Future` zurück, das entweder das Objekt enthält, oder `null`, wenn die ID nicht existiert. Alle Isar-Operationen sind standardmäßig asynchron, auch wenn die meisten ein synchrones Gegenstück haben:\n\n```dart\nfinal recipe = recipes.getSync(123);\n```\n\n:::warning\nNormalerweise solltest du die asynchrone Version der Methoden in deinem UI-Isolate bevorzugen. Da Isar sehr schnell ist, sind die synchronen Methoden oft auch in Ordnung.\n:::\n\nWenn du mehrere Objekte auf einmal abrufen möchtest, kannst du `getAll()` oder `getAllSync()` verwenden:\n\n```dart\nfinal recipe = await recipes.getAll([1, 2]);\n```\n\n### Abfragen von Objekten\n\nAnstatt Objekte über die ID zu erhalten, kannst du mittels `.where()` und `.filter()` auch eine Liste von Objekten abfragen, die bestimmten Bedingungen entsprechen:\n\n```dart\nfinal allRecipes = await recipes.where().findAll();\n\nfinal favourites = await recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ Lerne mehr: [Abfragen](queries)\n\n## Ändern der Datenbank\n\nJetzt ist es endlich an der Zeit, unsere Collection zu verändern! Um Objekte zu erstellen, zu aktualisieren oder zu löschen, rufe die entsprechenden Operationen innerhalb einer Schreibtransaktion auf:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await recipes.get(123)\n\n  recipe.isFavorite = false;\n  await recipes.put(recipe); // Aktualisierungsoperationen\n\n  await recipes.delete(123); // oder Löschoperationen durchführen\n});\n```\n\n➡️ Lerne mehr: [Transaktionen](transactions)\n\n### Objekt erstellen\n\nErstelle ein Objekt in einer Collection um es in Isar zu speichern. Die `put()`-Methode von Isar erstellt das Objekt entweder oder aktualisiert es, je nachdem, ob es bereits in der Collection existiert.\n\nWenn das ID-Feld `null` oder `Isar.autoIncrement` ist, verwendet Isar eine automatisch generierte ID.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await recipes.put(pancakes);\n})\n```\n\nIst das ID-Feld nicht-final, weist Isar die generierte ID automatisch dem Objekt zu.\n\nDas Erstellen von mehreren Objekten auf einmal ist genauso einfach:\n\n```dart\nawait isar.writeTxn(() async {\n  await recipes.putAll([pancakes, pizza]);\n})\n```\n\n### Objekt aktualisieren\n\nSowohl das Erstellen als auch das Aktualisieren funktioniert mit `collection.put(object)`. Wenn die ID `null` ist (oder nicht existiert), wird das Objekt erstellt, andernfalls wird es aktualisiert.\n\nWenn wir also Pfannkuchen nicht mehr mögen, können wir Folgendes tun:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await recipes.put(recipe);\n});\n```\n\n### Objekt löschen\n\nWillst du ein Objekt in Isar loswerden? Verwende `collection.delete(id)`. Die delete-Methode gibt zurück, ob ein Objekt mit der angegebenen ID gefunden und gelöscht wurde. Lass uns z.B. das Objekt mit der ID `123` löschen:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nÄhnlich wie bei `get()` und `put()` gibt es auch einen Massenlöschvorgang, der die Anzahl der gelöschten Objekte zurückgibt:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\nWenn du die IDs der zu löschenden Objekte nicht kennst ist es auch möglich eine Abfrage zu verwenden:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/de/faq.md",
    "content": "---\ntitle: FAQ\n---\n\n# Häufig gestellte Fragen\n\nEine zufällige Zusammenstellung an häufig gestellten Fragen zu Isar und Flutter-Datenbanken.\n\n### Warum brauche ich eine Datenbank?\n\n> Ich speichere meine Daten in einer Backend-Datenbank, warum benötige ich Isar?\n\nSogar heute kommt es vor, dass du keine Internetverbindung hast, wenn du in einer U-Bahn, einem Flugzeug oder zu Besuch bei deiner Oma bist, die kein WLAN und einen sehr schlechten Mobilfunkempfang hat. Du solltest deine App nicht durch schlechte Verbindung lahmlegen lassen.\n\n### Isar vs Hive\n\nDie Antwort ist leicht: Isar wurde [als Ersatz für Hive begonnen](https://github.com/hivedb/hive/issues/246) und ist nun an einem Punkt, wo ich immer empfehlen würde, Isar statt Hive zu benutzen.\n\n### Where-Klauseln?!\n\n> Warum muss **_ich_** wählen, welcher Index genutzt wird?\n\nEs gibt mehrere Gründe. Viele Datenbanken benutzen Heuristik um den besten Index für eine bestimmte Abfrage zu nutzen. Die Datenbank muss zusätzliche Nutzungsdaten sammeln (-> Overhead) und verwendet möglicherweise immer noch den falschen Index. Es dauert dadurch auch länger eine Abfrage zu starten.\n\nNiemand kennt deine Daten besser, als du, der Entwickler. Also kannst du den besten Index wählen und z.B. entscheiden, ob du einen Index zum Abfragen oder Sortieren verwenden willst.\n\n### Muss ich Indizes / Where-Klauseln benutzen?\n\nNö! Isar ist vermutlich schnell genug, auch wenn du nur Filter verwendest.\n\n### Ist Isar schnell genug?\n\nIsar ist unter den schnellsten Datenbanken für Mobilgeräte, also sollte es in den meisten Fällen schnellgenug sein. Wenn du auf Leistungsprobleme stößt, besteht die Möglichkeit, dass du was falschmachst.\n\n### Steigert Isar die Größe meiner App?\n\nJa, ein bisschen. Isar wird die Download-Größe deiner App um 1 - 1,5 MB erhöhen. Isar Web fügt nur wenige KB hinzu.\n\n### Die Docs sind falsch / Da ist ein Tippfehler\n\nOh nein, sorry. Bitte [öffne ein Issue](https://github.com/isar-community/isar/issues/new/choose), oder noch besser, mach einen PR um den Fehler zu beheben 💪.\n"
  },
  {
    "path": "docs/docs/de/indexes.md",
    "content": "---\ntitle: Indizes\n---\n\n# Indizes\n\nIndizes sind Isars mächtigstes Feature. Viele eingebettete Datenbanken bieten \"normale\" Indizes (wenn überhaupt), aber Isar hat auch Komposit- und Mehrfach-Indizes. Zu verstehen, wie Indizes funktionieren ist grundlegend um die Abfrageleistung zu optimieren. Isar lässt dich wählen welchen Index du verwenden möchtest und wie du ihn benutzen willst. Wir beginnen mit einer schnellen Einführung was Indizes sind.\n\n## Was sind Indizes?\n\nWenn eine Collection nicht indiziert ist, wird die Reihenfolge der Zeilen von der Abfrage aus sicherlich nicht als in irgendeiner Weise optimiert erkennbar sein. Daher muss die Abfrage linear alle Objekte durchsuchen. In anderen Worten, die Abfrage muss alle Objekte durchsuchen, um diejenigen zu finden, die zu den Bedingungen passen. Wie du dir bestimmt vorstellen kannst, kann das seine Zeit dauern. Durch jedes einzelne Objekt zu gucken ist nicht sehr effizient.\n\nZum Beispiel ist diese `Product`-Collection komplett unsortiert.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**Daten:**\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nEine Abfrage, die versucht alle Produkte zu finden, die mehr als 30€ kosten, muss alle neun Zeilen durchsuchen. Das ist kein Problem für nur neun Zeilen, aber könnte ein Problem für 100k Zeilen werden.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nUm die Leistung dieser Abfrage zu verbessern, indizieren wir die Eigenschaft `price`. Ein Index ist wie eine sortierte Nachschlagetabelle.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**Generierter Index:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nJetzt kann die Abfrage deutlich schneller durchgeführt werden. Es kann direkt zu den letzten drei Indexzeilen gesprungen werden und die entsprechenden Objekte anhand ihrer ID gefunden werden.\n\n### Sortierung\n\nEine andere coole Sache: Indizes können superschnell sortieren. Sortierte Abfragen sind kostenintensiv, weil die Datenbank alle Ergebnisse in den Speicher laden muss, bevor sie sortiert werden. Sogar wenn du einen Offset oder eine Limitierung angibst, werden diese erst nach dem Sortieren angewandt.\n\nStell dir vor, wir wollten die vier günstigsten Produkte finden. Wir könnten die folgende Abfrage verwenden:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nIn diesem Beispiel müsste die Datenbank alle (!) Objekte laden, sie nach dem Preis sortieren und die vier Produkte mit dem niedrigsten Preis zurückgeben.\n\nWie du dir vermutlich vorstellen kannst, kann das mit dem vorherigen Index sehr viel effizienter gemacht werden. Die Datenbank nimmt die ersten vier Zeilen des Indexes und gibt die zugehörigen Objekte zurück, da sie schon in der korrekten Reihenfolge sind.\n\nUm einen Index zum Sortieren zu verwenden würden wir die Abfrage so schreiben:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nDie `.anyX()` Where-Klausel teilt Isar mit, einen Index nur zum Sortieren zu verwenden. Du kannst also eine Where-Klausel wie `.priceGreaterThan()` benutzen und sortierte Ergenisse erhalten.\n\n## Eindeutige Indizes\n\nEin eindeutiger Index stellt sicher, dass der Index keine doppelten Werte enthält. Er kann aus einem oder mehreren Eigenschaften bestehen. Wenn ein eindeutiger Index eine Eigenschaft hat, sind die Werte dieser Eigenschaft eindeutig. Wenn ein eindeutiger Index mehr als eine Eigenschaft hat, dann ist die Kombination der Werte dieser Eigenschaften eindeutig.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nJeder Versuch Daten in einen eindeutigen Index einzufügen oder zu aktualisieren, die ein Dukplikat verursachen würden, resultieren in einem Fehler:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> Ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// Versucht einen Benutzer mit dem gleichen Benutzernamen einzufügen\nawait isar.users.put(user2); // -> Fehler: Eindeutigkeitsbeschränkung verletzt\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## Indizes ersetzen\n\nManchmal ist es nicht von Vorteil einen Fehler zu verursachen, wenn eine Eindeutigkeitsbeschränkung verletzt wird. Stattdessen möchtest du vielleicht das vorhandene Objekt mit dem Neuen ersetzen. Das kann erreicht werden, indem die Eigenschaft `replace` des Indexes auf `true` gesetzt wird.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nJetzt, wenn wir versuchen einen Benutzer mit einem vorhandenen Benutzernamen einzufügen, wird Isar den Vorhandenen mit dem neuen Benutzer ersetzen.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nErsetzbare Indizes generieren auch `putBy()`-Methoden, die es dir ermöglichen Objekte zu aktualisieren statt sie zu ersetzen. Die vorhandene ID wird wiederverwendet und Links bleiben erhalten.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// Nutzer existiert nicht, also ist es das gleiche wie put()\nawait isar.users.putByUsername(user1);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nWie du sehen kannst, wird die ID des zuerst eingefügten Benutzers wiederverwendet.\n\n## Indizes ohne Berücksichtigung auf Groß-/Kleinschreibung\n\nAlle Indizes auf `String`- und `List<String>`-Eigenschaften beachten standardmäßig die Groß-/Kleinschreibung. Wenn du einen Index erstellen willst, der die Groß-/Kleinschreibung nicht berücksichtigt, kannst du die `caseSensitive`-Option verwenden:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## Index-Typen\n\nEs gibt verschiedene Typen von Indizes. Meistens wirst du einen `IndexType.value`-Index verwenden wollen, aber Hash-Indizes sind effizienter.\n\n### Wert-Index\n\nWert-Indizes sind der Standardtyp und der Einzige, der für alle Eigenschaften erlaubt ist, die nicht Strings oder Listen enthalten. Eigenschaftswerte werden verwendet, um den Index zu erstellen. Im Fall von Listen, werden die Elemente der Liste verwendet. Es ist der flexibelste, aber auch platzraubendste der drei Index-Typen.\n\n:::tip\nBenutze `IndexType.value` für Primitives, Strings, wenn du `startsWith()`-Where-Klauseln brauchst, und Listen, wenn du nach einzelnen Elementen suchst.\n:::\n\n### Hash-Index\n\nStrings und Listen können gehasht werden um den für den Index benötigten Speicher drastisch zu verringern. Der Nachteil eines Hash-Indexes ist, dass sie nicht für Präfixsuchen (`startsWith()`-Where-Klauseln) verwendet werden können.\n\n:::tip\nVerwende `IndexType.hash` für Strings und Listen, wenn du die `startsWith`- und `elementEqualTo`-Where-Klauseln nicht benötigst.\n:::\n\n### HashElements-Index\n\nStringlisten können als Ganzes gehasht werden (indem man `IndexType.hash` verwendet) oder die Elemente der Liste können seperat gehasht werden (indem man `IndexType.hashElements` nutzt) wodurch ein Mehreintragsindex mit gehashten Elementen erzeugt wird.\n\n:::tip\nNutze `IndexType.hashElements` für `List<String>` bei denen du `elementEqualTo`-Where-Klauseln benötigst.\n:::\n\n## Komposit-Indizes\n\nEin Komposit-Index ist ein Index auf mehrere Eigenschaften. Isar erlaubt es dir zusammengesetzte Indizes mit bis zu drei Eigenschaften zu erstellen.\n\nKomposit-Indizes sind auch als Mehr-Spalten-Indizes bekannt.\n\nEs ist vermutlich am besten mit einem Beispiel zu starten. Wir erstellen eine Personen-Collection und definieren einen zusammengesetzten Index auf die Alters- und Namenseigenschaften:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**Daten:**\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**Generierter Index:**\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nDer generierte zusammengesetzte Index enthält alle Personen sortiert nach ihrem Alter und ihrem Namen.\n\nKomposit-Indizes sind super, wenn du effiziente Abfragen, sortiert nach mehreren Eigenschaften, stellen willst. Sie erlauben auch anspruchsvolle Where-Klauseln mit mehreren Eigenschaften:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nDie letzte Eigenschaft eines zusammengesetzten Index unterstützt auch Bedingungen wie `startsWith()` oder `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Mehrfach-Indizes\n\nWenn du eine Liste mit `IndexType.value` indizierst, wird Isar automatische einen Mehrfach-Index erzeugen und jeder Eintrag in der Liste wird mit dem Objekt indiziert. Das funktioniert für alle Listentypen.\n\nZu sinnvollen Anwendungen für Mehrfach-Indizes zählen das Indizieren einer Liste an Tags oder einen Volltext-Index zu erstellen.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` trennt einen String nach der [Unicode Annex #29](https://unicode.org/reports/tr29/)-Spezifikation in Worte, sodass es für fast alle Sprachen richtig funktioniert.\n\n**Daten:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nEinträge mit doppelten Worten tauchen nur einmal im Index auf.\n\n**Generierter Index:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nDieser Index kann nun für (Gleichheits- oder) Präfix-Where-Klauseln der individuellen Worte der Beschreibung verwendet werden.\n\n:::tip\nStatt Worte direkt zu speichern kannst du auch in Betracht ziehen das Ergebnis einer [Phonetischen Suche](https://de.wikipedia.org/wiki/Phonetische_Suche) wie von dem Algorithmus [Soundex](https://de.wikipedia.org/wiki/Soundex) zu verwenden.\n:::\n"
  },
  {
    "path": "docs/docs/de/limitations.md",
    "content": "---\ntitle: Limitationen\n---\n\n# Limitationen\n\nWie du weißt, funktioniert Isar auf Mobilgeräten und Desktops und läuft sowohl auf der VM, als auch im Web. Die beiden Plattformen sind sehr verschieden und haben unterschiedliche Limitationen.\n\n## VM Limitationen\n\n- Nur die ersten 1024 Bytes eines Strings können für eine Präfix-Where-Klausel verwendet werden\n- Objekte können höchstens 16MB groß sein\n\n## Web Limitationen\n\nWeil Isar Web auf IndexedDB beruht, gibt es dort mehr Limitationen, aber sie sind kaum zu merken, während du Isar benutzt.\n\n- Synchrone Methoden werden nicht unterstützt\n- Zurzeit sind die `Isar.splitWords()`- und `.matches()`-Filter noch nicht implementiert\n- Schemaänderungen werden nicht so genau wie in der VM überprüft, also achte darauf die Regeln einzuhalten\n- Alle Zahlen-Typen werden als Double (dem einzigen JS Zahlen-Typ) gespeichert, also hat `@Size32` keine Wirkung\n- Indizes werden anders dargestellt, wodurch Hash-Indizes nicht weniger Platz benötigen (auch wenn sie gleich funktionieren)\n- `col.delete()` und `col.deleteAll()` funktionieren korrekt, aber der Rückgabewert ist nicht richtig\n- `col.clear()` setzt den auto-increment-Wert nicht zurück\n- `NaN` wird als Wert nicht unterstützt\n"
  },
  {
    "path": "docs/docs/de/links.md",
    "content": "---\ntitle: Links\n---\n\n# Links\n\nLinks ermöglichen es dir Verhältnisse zwischen Objekten, wie z.B. dem Autor (Benutzer) eines Kommentars, auszudrücken. Du kannst `1:1`, `1:n`, `n:m` Verhältnisse mit Isar-Links modellieren. Links zu nutzen ist unpraktischer als eingebettete Objekte zu benutzen, und du solltest eingebettete Objekte, wann immer möglich, verwenden.\n\nStell dir den Link wie eine separate Tabelle vor, die die Beziehung enthält. Links ähneln SQL-Beziehungen, haben aber einen anderen Funktionsumfang und eine andere API.\n\n## IsarLink\n\n`IsarLink<T>` kann keines oder ein zugehöriges Objekt enthalten und kann genutzt werden um eine zu-einem-Relation darzustellen. `IsarLink` hat eine einzige Eigenschaft genannt `value`, die das verlinkte Objekt enthält.\n\nLinks sind lazy, also musst du dem `IsarLink` explizit sagen den `value` zu Laden oder zu Speichern. Das kannst du erreichen, indem du `linkProperty.load()` und `linkProperty.save()` aufrufst.\n\n:::tip\nDie ID-Eigenschaft der Quell- und Ziel-Collections sollte nicht-final sein.\n:::\n\nFür nicht-Web-Ziele werden Links automatisch geladen, wenn du sie zum ersten Mal verwendest. Fangen wir damit an einen IsarLink zu einer Collection hinzuzufügen:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nWir haben einen Link zwischen Lehrern und Schülern definiert. Jeder Schüler kann in diesem Beispiel genau einen Lehrer haben.\n\nZuerst legen wir einen Lehrer an und fügen ihn dann einem Schüler hinzu. Wir müssen den Lehrer mit der `.put()`-Methode einfügen und den Link manuell speichern.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teachers.save();\n});\n```\n\nWir können den Link jetzt nutzen:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nVersuchen wir das gleiche mit synchronem Code. Wir brauchen den Link nicht manuell zu speichern, weil `.putSync()` automatisch alle Links speichert. Es erzeugt sogar den Lehrer für uns.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nEs würde mehr Sinn ergeben, wenn der Schüler aus dem vorherigen Beispiel mehrere Lehrer haben kann. Glücklicherweise hat Isar `IsarLinks<T>`, was mehrere zugehörige Objekte beinhalten kann und eine zu-vielen-Relation ausdrückt.\n\n`IsarLinks<T>` wird von `Set<T>` erweitert und stellt alle Methoden die auf Sets angewandt werden können zur Verfügung.\n\n`IsarLinks` verhält sich ähnlich wie `IsarLink` und ist auch lazy. Um alle verlinkten Objekte zu laden, musst du die Methode `linkProperty.load()` aufrufen. Um die Änderungen persistent zu machen, musst du `linkProperty.save()` aufrufen.\n\nIntern werden `IsarLink` und `IsarLinks` auf die gleiche Weise dargestellt. Wir können den `IsarLink<Teacher>` von vorher zu einem `IsarLinks<Teacher>` ausbauen, um mehrere Lehrer einem einzelnen Schüler zuzuweisen (ohne Daten zu verlieren).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLinks<Teacher>();\n}\n```\n\nDas funktioniert, weil wir den Namen des Links (`teacher`) nicht verändert haben, weshalb sich Isar von vorher daran erinnert.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Rückverlinkungen\n\nIch höre dich schon, \"Was, wenn wir umgekehrte Relationen ausdrücken möchten?\", fragen. Mach dir keine Sorgen; wir führen jetzt Rückverlinkungen ein.\n\nRückverlinkungen sind Links in umgekerhrter Richtung. Jeder Link hat implizit immer eine Rückverlinkung. Du kannst sie in deiner App verfügbar machen, indem du `IsarLink` oder `IsarLinks` mit `@Backlink()` annotierst.\n\nRückverlinkungen benötigen keinen zusätzlichen Speicher oder Ressourcen; du kannst sie frei hinzufügen, löschen und umbenennen, ohne Daten zu verlieren.\n\nWir wollen wissen, welche Schüler ein spezifischer Lehrer hat, also definieren wir eine Rückverlinkung:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nWir müssen angeben, auf welchen Link die Rückverlinkung zeigt. Es ist möglich, mehrere verschiedene Links zwischen zwei Objekten zu haben.\n\n## Links initialisieren\n\n`IsarLink` und `IsarLinks` haben Konstruktoren ohne Argumente und sollten verwendet werden um die Link-Eigenschaft anzugeben, wenn das Objekt erstellt wird. Es hat sich bewährt Link-Eigenschaften `final` zu setzen.\n\nWenn du dein Objekt zum ersten Mal mit der `put()`-Methode speicherst, wird der Link mit Quell- und Ziel-Collection initialisiert und du kannst Methoden wie `load()` und `save()` benutzen. Ein Link fängt sofort an Änderungen zu verfolgen, nachdem er erzeugt wurde, sodass du Relationen sogar anlegen oder entfernen kannst, bevor der Link initialisiert wurde.\n\n:::danger\nEs ist verboten einen Link zu einem anderen Objekt zu übertragen.\n:::\n"
  },
  {
    "path": "docs/docs/de/queries.md",
    "content": "---\ntitle: Abfragen\n---\n\n# Abfragen\n\nMit Abfragen kannst du Einträge finden, die bestimmten Bedingungen entsprechen, zum Beispiel:\n\n- Finde alle markierten Kontakte\n- Finde eindeutige Vornamen in den Kontakten\n- Lösche alle Kontakte, die keinen Nachnamen definiert haben\n\nWeil Abfragen nicht in Dart, sondern auf der Datenbank ausgeführt werden, sind sie sehr schnell. Wenn du Indizes sinnvoll benutzt, kannst du deine Abfrageleistung sogar weiter steigern. Als nächstes lernst du, wie man Abfragen schreibt und wie du sie so schnell wie möglich machen kannst.\n\nEs gibt zwei verschiedene Methoden um Einträge zu filtern: Filter und Where-Klauseln. Wir beginnen indem wir uns die Funktionsweise von Filtern ansehen.\n\n## Filter\n\nFilter sind leicht zu benutzen und zu verstehen. Abhängig von den Typen deiner Eigenschaften gibt es verschiedene verfügbare Filteroperationen mit größtenteils selbsterklärenden Namen.\n\nFilter funktionieren, indem sie einen Ausdruck für jedes Objekt der zu filternden Collection auswerten. Wenn der Ausdruck `true` ergibt, fügt Isar das Objekt zu den Ergebnissen hinzu.\nFilter haben keinen Einfluss auf die Reihenfolge der Ergebnisse.\n\nWir benutzen das folgende Modell für die Beispiele weiter unten:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### Abfragebedingungen\n\nAbhängig vom Feld-Typen gibt es verschiedene mögliche Bedingungen.\n\n| Bedingung                | Beschreibung                                                                                                                                        |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `.equalTo(value)`        | Trifft auf Werte zu, die mit dem angegebenen `value` übereinstimmen.                                                                                |\n| `.between(lower, upper)` | Trifft auf Werte zu, die zwischen `lower` und `upper` liegen.                                                                                       |\n| `.greaterThan(bound)`    | Trifft auf Werte zu, de größer als `bound` sind.                                                                                                    |\n| `.lessThan(bound)`       | Trifft auf Werte zu, die kleiner als `bound` sind. `null`-Werte werden eingeschlossen, da `null` als kleiner als jeder andere Wert betrachtet wird. |\n| `.isNull()`              | Trifft auf Werte zu, die `null` sind.                                                                                                               |\n| `.isNotNull()`           | Trifft auf Werte zu, die nicht `null` sind.                                                                                                         |\n| `.length()`              | Abfragen nach Längen von Listen, Strings und Links filtern Objekte basierend auf der Anzahl der Elemente in einer Liste oder in einem Link.         |\n\nNehmen wir an, dass die Datenbank vier Schuhe mit den Gößen 39, 40, 46 und einen mit einer nicht festgelegten Größe (`null`) hat. Wenn du keine Sortierung durchführst, werden die Werte nach ID geordnet zurückgegeben.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### Logische Operatoren\n\nDu kannst Bedingungen verbinden, indem du die folgenden logischen Operatoren verwendest:\n\n| Operator   | Beschreibung                                                                         |\n| ---------- | ------------------------------------------------------------------------------------ |\n| `.and()`   | Ergibt `true`, wenn von linkem und rechtem Ausdruck beide `true` ergeben.            |\n| `.or()`    | Ergibt `true`, wenn mindestens einer von beiden Ausdrücken `true` ergibt.            |\n| `.xor()`   | Ergibt `true`, wenn genau einer von beiden Ausdrücken `true` ergibt.                 |\n| `.not()`   | Negiert das Ergebnis des nachfolgenden Ausdrucks.                                    |\n| `.group()` | Gruppiert Bedingungen und ermöglicht es eine Reihenfolge der Auswertung festzulegen. |\n\nWenn du alle Schuhe mit der Größe 46 finden möchstest, kannst du die folgende Abfrage verwenden:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nWenn du mehr als eine Bedingung angeben möchtest, kannst du mehrere Filter verbinden, indem du sie mit logischem **und** `.and()`, logischem **oder** `.or()` oder logischem **exklusiven oder** `.xor()` verbindest.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Optional. Filter werden implizit mit einem logischen UND verbunden.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nDiese Abfrage ist äquivalent zu: `size == 46 && isUnisex == true`.\n\nDu kannst auch Bedingungen gruppieren, indem du `.group()` benutzt:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nDiese Abfrage ist äquivalent zu: `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nUm eine Bedingung oder Gruppe zu negieren kannst du das logische **oder** `.not()` verwenden:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nDiese Abfrage ist äquivalent zu: `size != 46 && isUnisex != true`.\n\n### String-Bedingungen\n\nZusätzlich zu den vorher genannten Abfragebedingungen, bieten String-Werte ein paar mehr Bedingungen. Platzhalter, ähnlich zu beispielsweise Regex, erlauben mehr Flexibilität beim Suchen.\n\n| Bedingung            | Beschreibung                                                                   |\n| -------------------- | ------------------------------------------------------------------------------ |\n| `.startsWith(value)` | Trifft auf String-Werte zu, die mit dem angegebenen `value` beginnen.          |\n| `.contains(value)`   | Trifft auf String-Werte zu, die das angegebene `value` enthalten.              |\n| `.endsWith(value)`   | Trifft auf String-Werte zu, die mit dem angegebenen `value` enden.             |\n| `.matches(wildcard)` | Trifft auf String-Werte zu, die dem angegebenen `wildcard`-Muster entsprechen. |\n\n**Groß-/Kleinschreibung**  \nAlle String-Operationen haben eine optionale `caseSensitive`-Eigenschaft, die standardmäßig `true` ist.\n\n**Platzhalter**  \nDer [Ausdruck eines Platzhalter-Strings](https://de.wikipedia.org/wiki/Wildcard_(Informatik)) ist ein String, der normale Zeichen mit zwei speziellen Platzhalter-Zeichen verwendet:\n\n- Der `*` Platzhalter trifft auf keines oder mehr von jedem Zeichen zu.\n- Der `?` Platzhalter trifft auf jedes Einzelzeichen zu.  \n  Zum Beispiel trifft der Platzhalter-String `\"d?g\"` auf `\"dog\"`, `\"dig\"` und `\"dug\"` zu, nicht aber auf `\"ding\"`, `\"dg\"` oder `\"a dog\"`.\n\n### Abfragemodifikatoren\n\nManchmal ist es notwendig eine Abfrage auf Bedingungen aufzubauen oder für verschiedene Werte zu bauen. Isar hat ein sehr mächtiges Werkzeug um bedingte Abfragen zu bauen:\n\n| Modifikator           | Beschreibung                                                                                                                                                                                      |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `.optional(cond, qb)` | Erweitert die Abfrage nur, wenn die Bedingung `cond`, `true` ist. Das kann fast überall in einer Abfrage verwendet werden, beispielsweise um sie über eine Bedingung zu sortieren oder begrenzen. |\n| `.anyOf(list, qb)`    | Erweitert die Abfrage für jeden Wert in `values` und verbindet die Bedingungen mit einem logischen **oder**.                                                                                      |\n| `.allOf(list, qb)`    | Erweitert die Abfrage für jeden Wert in `values` und verbindet die Bedingungen mit einem logischen **und**.                                                                                       |\n\nIn diesem Beispiel bauen wir eine Methode, die Schuhe mit einem optionale Filter finden kann:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // Wendet den Filter nur an, wenn sizeFilter != null ist\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nWenn du alle Schuhe finden möchtest, die eine von mehreren Schuhgrößen haben, kannst du entweder eine konventionelle Abfrage schreiben oder den `anyOf()` Modifikator verwenden:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nAbfragemodifikatoren sind besonders dann sinnvoll, wenn du dynamische Abfragen bauen möchtest.\n\n### Listen\n\nAbfragen können sogar auf Listen gestellt werden:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nDu kannst eine Abfrage auf Basis der Listenlänge bauen:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nDiese sind äquivalent zu dem Dart-Code `tweets.where((t) => t.hashtags.isEmpty);` und `tweets.where((t) => t.hashtags.length > 5);`. Du kannst auch Abfragen basierend auf Listenelementen stellen:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nDas ist äquivalent zum Dart-Code `tweets.where((t) => t.hashtags.contains('flutter'));`.\n\n### Eingebettete Objekte\n\nEingebettete Objekte sind eines von Isars nützlichsten Features. Sie können sehr einfach abgefragt werden mit gleichen Bedingungen für Objekte der obersten Ebene. Nehmen wir an, dass wir das folgende Modell haben:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nWir wollen alle Autos abfragen, die eine Marke mit dem Namen `\"BMW\"` und dem Land `\"Germany\"` haben. Wir können das mit der folgenden Abfrage erreichen:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nVersuche immer verschachtelte Abfragen zu gruppieren. Die vorherige Abfrage ist effizienter als die folgende, auch wenn das Ergebnis gleich ist:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### Links\n\nWenn dein Modell [Links oder Rückverlinkungen](links) enthält, kannst du deine Abfrage auf Basis der verlinkten Objekte oder der Anzahl an verlinkten Objekten filtern.\n\n:::warning\nBeachte, dass Link-Abfragen teuer sein können, weil Isar die verlinkten Objekte abrufen muss. Versuche stattdessen eingebettete Objekte zu verwenden.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nWir wollen alle Schüler finden, die einen Mathe- oder Englischlehrer haben:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nLink-Filter resultieren in `true`, wenn mindestens eines der verlinkten Objekte den Bedingungen entspricht.\n\nSuchen wir nach allen Schülern, die keine Lehrer haben:\n\n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\noder alternativ:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Where-Klauseln\n\nWhere-Klauseln sind eine sehr mächtiges Werkzeug, aber es kann ein bisschen herausfordernd sein sie zu meistern.\n\nIm Gegensatz zu Filtern nutzen Where-Klauseln die Indizes, die du im Schema definiert hast, um die Abfragebedingungen zu überprüfen. Einen Index abzufragen ist deutlich schneller als jeden Eintrag einzeln zu filtern.\n\n➡️ Lerne mehr: [Indizes](indexes)\n\n:::tip\nAls eine einfache Regel solltest du immer versuchen die Einträge so weit wie möglich mit Where-Klauseln einzugrenzen und das restliche Filtern mit Filtern machen.\n:::\n\nDu kannst Where-Klauseln nur mit logischem **oder** verbinden. In anderen Worten, kannst du mehrere Where-Klauseln zusammenfügen, aber nicht die Überschneidung mehrerer Where-Klauseln abfragen.\n\nLass uns Indizes zu der Schuh-Collection hinzufügen:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nHier gibt es zwei Indizes. Der Index auf `size` erlaubt es uns Where-Klauseln wie `.sizeEqualTo()` zu verwenden. Der zusammengesetzte Index auf `isUnisex` erlaubt es uns Where-Klauseln wie `.isUnisexSizeEqualTo()` zu nutzen. Aber auch `.isUnisexEqualTo()` ist möglich, weil du immer jedes Präfix eines Indexes benutzen kannst.\n\nWir können unsere Abfrage von vorher, die Unisex-Schuhe der Größe 46 findet, also mithilfe des zusammengesetzten Indexes umschreiben. Diese Abfrage sollte deutlich schneller sein, als die vorherige:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nWhere-Klauseln haben zwei weitere Superkräfte: Sie geben dir \"kostenloses\" Sortieren und eine superschnelle Eindeutigkeitsoperation.\n\n### Where-Klauseln und Filter verbinden\n\nErinnerst du dich an die `shoes.filter()`-Abfragen? Das ist in Wirklichkeit nur eine Kurzform für `shoes.where().filter()`. Du kannst (und solltest) Where-Klauseln und Filter in der gleichen Abfrage verbinden, um die Vorteile beider zu nutzen:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nDie Where-Klausel wird zuerst angewendet, um die Anzahl an Objekten, die gefiltert werden müssen, zu reduzieren. Dann wird der Filter auf die übrig gebliebenen Objekte angewendet.\n\n## Sortierung\n\nDu kannst definieren, wie Ergebnisse deiner Abfrage sortiert werden sollen, indem du die Methoden `.sortBy()`, `.sortByDesc()`, `.thenBy()` und `.thenByDesc()` nutzt.\n\nUm alle Schuhe nach Modellnamen in aufsteigender und nach der Größe in absteigender Reihenfolge sortiert zu bekommen, ohne einen Index zu benutzen, aknnst du die folgende Abfrage stellen:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nViele Ergebnisse zu sortieren kann teuer sein, besonders, weil das Sortieren vor dem Offset und vor der Limitierung stattfindet. Die Sortiermethoden benutzen niemals Indizes. Glücklicherweise können wir wieder Sortierung mit Where-Klauseln verwenden und so unsere Abfrage blitzschnell machen, auch wenn wir eine Million Objekte sortieren müssen.\n\n### Sortierung mit Where-Klauseln\n\nWenn du eine **einzige** Where-Klausel in deiner Abfrage nutzt, sind die Ergebnisse schon nach dem Index sortiert. Das ist eine große Sache!\n\nNehmen wir an, wir haben Schuhe in den Größen `[43, 39, 48, 40, 42, 45]` und wir wollen alle Schuhe mit einer Größe größer als `42` haben und sie auch nach Größe sortiert haben:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // Sortiert die Ergebnisse auch nach Größe\n  .findAll(); // -> [43, 45, 48]\n```\n\nWie du sehen kannst, sind die Ergebnisse nach dem `size`-Index sortiert. Wenn du die Reihenfolge der Where-Klausel umkehren möchtest, kannst du `sort` auf `Sort.desc` setzen:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nManchmal willst du keine Where-Klausel verwenden, aber trotzdem von der impliziten Sortierung profitieren. Dann kannst du die Where-Klausel `any` verwenden:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nWenn du einen Komposit-Index verwendest, werden die Ergebnisse nach allen Feldern des Indexes sortiert.\n\n:::tip\nFür den Fall, dass deine Ergebnisse sortiert sein müssen, versuche einen Index zu benutzen. Besonders wenn du mit `offset()` oder `limit()` arbeitest:\n:::\n\nManchmal ist es nicht möglich oder sinnvoll einen Index zum Sortieren zu nutzen. Für solche Fälle solltest du Indizes benutzen, um zumindest die Anzahl an zu sortierenden Einträgen so weit wie möglich einzugrenzen.\n\n## Eindeutige Werte\n\nUm nur Einträge mit eindeutigen Werten zurückzubekommen, kannst du das Unterscheidbarkeitsprädikat verwenden. Zum Beispiel, um herauszufinden, wie viele unterscheidbare Schuhmodelle es in deiner Isar-Datenbank gibt:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nDu kannst auch mehrere Unterscheidbarkeitsbedingungen verketten, um alle Schuhe mit unterscheidbaren Modell-Größe-Kombinationen zu finden:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nNur das erste Ergebnis jeder Unterscheidbarkeitskombination wird zurückgegeben. Um das zu überprüfen kannst du Where-Klauseln und Sortieroperationen verwenden.\n\n### Unterscheidbare Where-Klauseln\n\nWenn du einen nicht eindeutigen Index hast, kann es sein, dass du alle seine unterscheidbaren Werte haben möchtest. Du könntest die `distinctBy`-Operation des vorherigen Abschnitts verwenden, aber sie wird erst nach dem Sortieren und Filtern angewandt, sodass ein bisschen Overhead entsteht.\nWenn du nur eine einzelne Where-Klausel verwendest, kannst du stattdessen dem Index vertrauen die Unterscheidbarkeitsoperation durchzuführen.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nTheoretisch könntest du sogar mehrere Where-Klauseln für Sortierung und Unterscheidbarkeit nutzen. Die einzige Einschränkung besteht darin, dass sich diese Where-Klauseln nicht überschneiden, also nicht denselben Index verwenden dürfen. Für die richtige Sortierung müssen sie auch in Sortierreihenfolge angewandt werden. Sei sehr vorsichtig, wenn du dich darauf verlässt.\n:::\n\n## Offset & Limitierung\n\nEs ist oft eine gute Idee die Anzahl an Ergebnissen einer Abfrage zu beschränken, für beispielsweise lazy Listenansichten. Du kannst das erreichen, indem du ein `limit()` setzt:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nIndem du ein `offset()` setzt, kannst du die Ergebnisse deiner Abfrage in mehrere Auflistungen aufteilen.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nWeil das instanziieren eines Dart-Objekts meistens der teuerste Teil beim Ausführen einer Abfrage ist, ist es eine gute Idee nur die Objekte zu laden, die du benötigst.\n\n## Reihenfolge der Ausführung\n\nIsar führt Abfragen immer in der gleichen Reihenfolge aus:\n\n1. Primär- oder Sekundärindex durchlaufen, um Objekte zu finden (Where-Klauseln anwenden)\n2. Objekte filtern\n3. Ergebnisse sortieren\n4. Unterscheidbarkeitsoperation durchführen\n5. Offset & Limit auf Ergebnisse anwenden\n6. Ergebnisse zurückgeben\n\n## Abfrageoperationen\n\nIn den vorangegangenen Beispielen haben wir `.findAll()` verwendet, um alle passenden Objekte zu erhalten. Es sind jedoch mehr Operationen verfügbar:\n\n| Operation        | Beschreibung                                                                                                                                           |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `.findFirst()`   | Erhalte nur das erste passende Objekt oder `null` wenn kein passendes gefunden wird.                                                                   |\n| `.findAll()`     | Erhalte alle passenden Objekte.                                                                                                                        |\n| `.count()`       | Zählt, wieviele Objekte der Abfrage entsprechen.                                                                                                       |\n| `.deleteFirst()` | Löscht das erste passende Objekt aus der Collection.                                                                                                   |\n| `.deleteAll()`   | Löscht alle passenden Objekte aus der Collection.                                                                                                      |\n| `.build()`       | Konstruiert eine Abfrage um sie später wiederzuverwenden. Das erspart die Kosten, eine Abfrage erneut zu bauen, wenn du sie mehrfach ausführen willst. |\n\n## Abfragen auf Eigenschaften\n\nWenn du nur an den Werten einer bestimmten Eigenschaft interessiert bist, kannst du Abfragen auf Eigenschaften machen. Baue einfach eine normale Abfrage und wähle eine Eigenschaft:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nNur eine einzige Eigenschaft zu nutzen erspart Zeit bei der Deserialisierung. Abfragen auf Eigenschaften funktionieren auch bei eingebetteten Objekten und Listen.\n\n## Aggregation\n\nIsar unterstützt die Aggregation der Werte einer Abfrage auf Eigenschaften. Die folgenden Aggregatoroperationen sind verfügbar:\n\n| Operation    | Beschreibung                                                         |\n| ------------ | -------------------------------------------------------------------- |\n| `.min()`     | Findet den minimalen Wert oder `null`, wenn keiner passt.            |\n| `.max()`     | Findet den maximalen Wert oder `null`, wenn keiner passt.            |\n| `.sum()`     | Addiert alle Werte.                                                  |\n| `.average()` | Berechnet den Durchschnitt aller Werte oder `NaN` wenn keiner passt. |\n\nAggregatoren zu nutzen ist deutlich schneller, als alle passenden Objekte zu finden und die Aggregation manuell durchzuführen.\n\n## Dynamische Abfragen\n\n:::danger\nDieser Abschnitt ist höchstwahrscheinlich nicht wichtig für dich. Es ist davon abzuraten dynamische Abfragen zu nutzen, es sei denn du benötigst sie wirklich (was selten vorkommt).\n:::\n\nAlle der vorherigen Beispiele haben den QueryBuilder und seine statischen Erweiterungsmethoden genutzt. Vielleicht möchtest du dynamische Abfragen oder eine benutzerdefinierte Abfragesprache (wie den Isar Inspektor) bauen. In dem Fall kannst du die Methode `buildQuery()` verwenden:\n\n| Parameter       | Beschreibung                                                                                               |\n| --------------- | ---------------------------------------------------------------------------------------------------------- |\n| `whereClauses`  | Die Where-Klauseln der Abfrage.                                                                            |\n| `whereDistinct` | Ob Where-Klauseln nur unterscheidbare Werte zurückgeben sollen (nur sinnvoll für einzelne Where-Klauseln). |\n| `whereSort`     | Die Durchlaufreihenfolge der Where-Klauseln (nur sinnvoll für einzelne Where-Klauseln).                    |\n| `filter`        | Die Filter, die auf die Ergebnisse angewendet werden sollen.                                               |\n| `sortBy`        | Eine Liste an Eigenschaften nach denen sortiert werden soll.                                               |\n| `distinctBy`    | Eine Liste an Eigenschaften, an denen die Unterscheidbarkeit festgemacht wird.                             |\n| `offset`        | Der Offset der Ergebnisse.                                                                                 |\n| `limit`         | Die maximale Anzahl an Ergebnissen, die zurückgegeben werden.                                              |\n| `property`      | Wenn nicht-null, werden nur die Werte dieser Eigenschaft zurückgegeben.                                    |\n\nBauen wir eine dynamische Abfrage:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nDie folgende Abfrage ist äquivalent:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/de/recipes/data_migration.md",
    "content": "---\ntitle: Migration von Daten\n---\n\n# Migration vron Daten\n\nIsar migriert deine Datenbankschemas automatisch, wenn du Collections, Felder oder Indizes hinzufügst oder entfernst. Manchmal möchtest du möglicherweise auch deine Daten migrieren. Isar liefert keine eingebaute Lösung, weil das willkürliche Migrationsbeschränkungen festlegen würde. Es ist leicht eine passende Migrationslogik zu implementieren.\n\nWir wollen in diesem Beispiel eine einzige Version für die gesamte Datenbank verwenden. Wir benutzen Shared Preferences um die derzeitige Version zu speichern und vergleichen diese mit der Version zu der wir unsere Daten migrieren wollen. Wenn die Versionen nicht übereinstimmen, migrieren wir die Daten und aktualisieren die Version.\n\n:::tip\nDu kannst auch jeder Collection seine eigene Version zuweisen und sie individuell migrieren.\n:::\n\nStell dir vor, wie haben eine Benutzer-Collection mit einem Feld Geburtstag. In Version 2 unserer App benötigen wir ein zusätzliches Feld Geburtsjahr um Benutzer anhand des Alters abzufragen.\n\nVersion 1:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersion 2:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\nDas Problem ist, dass vorhandene Benutzermodelle ein leeres `birthYear`-Feld haben werden, weil es in Version 1 nicht existiert hat. Wir müssen die Daten migrieren, um das `birthYear`-Feld zu setzen.\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // Wenn die Version nicht gesetzt (neue Installation), oder schon 2 ist, müssen wir nicht migrieren\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // Version aktualisieren\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // Wir paginieren durch die Benutzer, um zu vermeiden, dass wir alle Benutzer gleichzeitig in den Speicher laden\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // Wir müssen nichts aktualisieren, weil der birthYear-Getter verwendet wird\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\nWenn du viele Daten migrieren musst, solltest du überlegen einen Hintergrund-Isolate zu verwenden, um eine Belastung des UI-Threads zu verhindern.\n:::\n"
  },
  {
    "path": "docs/docs/de/recipes/full_text_search.md",
    "content": "---\ntitle: Volltextsuche\n---\n\n# Volltextsuche\n\nVolltextsuche ist ein mächtiges Werkzeug um Text in der Datenbank zu suchen. Du solltest schon damit vertraut sein, wie [Indizes](/indexes) funktionieren, aber wir schauen uns die Grundlagen an.\n\nEin Index funktioniert wie eine Nachschlagetabelle, die es der Abfrage-Engine ermöglicht Einträge mit einem bestimmten Wert schnell zu finden. Zum Beispiel, wenn du ein `title`-Feld in deinem Objekt hast, kannst du einen Index auf das Feld anlegen, um die Geschwindigkeit zu erhöhen, ein Objekt mit bestimmtem Titel zu finden.\n\n## Warum ist Volltextsuche sinnvoll?\n\nDu kannst Text leicht durchsuchen, indem du Filter verwendest. Es gibt mehrere unterschiedliche String-Operationen, zum Beispiel `.startsWith()`, `.contains()` und `.matches()`. Das Problem mit Filtern ist, dass ihre Laufzeit `O(n)` ist, wobei `n` die Anzahl der Einträge in der Collection ist. String-Operationen wie `.matches()` sind besonders teuer.\n\n:::tip\nVolltextsuche ist deutlich schneller als Filter, aber Indizes haben ein paar Einschränkungen. In diesem Rezept wollen wir uns angucken, wie man diese Limitationen umgeht.\n:::\n\n## Grundlegendes Beispiel\n\nDie Idee ist immer die Gleiche: Anstatt den ganzen Text zu indizieren, indizieren wir die Worte im Text, sodass wir individuell nach ihnen suchen können.\n\nBauen wir den grundlegendsten Volltext-Index:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nWir können jetzt nach Nachrichten suchen, die spezifische Worte enthalten:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nDiese Abfrage ist superschnell, aber es gibt ein paar Probleme:\n\n1. Wir können nur nach ganzen Worten suchen\n2. Wir missachten Zeichensetzung\n3. Wir unterstützen keine anderen Leerzeichen\n\n## Text richtig trennen\n\nVersuchen wir das vorherige Beispiel zu verbessern. Wir könnten versuchen einen komplizierten Regex zu entwickeln, um Worte zu trennen, aber das ist vermutlich langsam und in Grenzfällen falsch.\n\nDer [Unicode Annex #29](https://unicode.org/reports/tr29/) definiert wie man, für fast alle Sprachen, Text richtig in Worte trennt. Das ist ziemlich kompliziert, aber glücklicherweise macht Isar den schwierigsten Teil der Arbeit für uns:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## Ich will mehr Kontrolle\n\nDas ist kinderleicht! Wir können unseren Index so ändern, dass er auch Präfixe findet und Groß-/Kleinschreibung ignoriert:\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nIsar speichert die Worte standardmäßig als gehashte Werte, was schnell und platzsparend ist. Aber Hashes können nicht für die Präfixüberprüfung verwendet werden. Wenn wir `IndexType.value` verwenden, können wir den Index ändern, um direkt Worte zu benutzen. Das ermöglicht uns die `.titleWordsAnyStartsWith()`-Where-Klausel benutzen zu können:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## Ich brauche auch `.endsWith()`\n\nKlar! Wir werden einen Trick verwenden, um `.endsWith()` verwenden zu können:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nVergiss nicht das Wortende umzukehren nach dem du suchen willst:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Abstammungsalgorithmen\n\nLeider unterstützen Indizes nicht die `.contains()`-Methode (das stimmt auch für andere Datenbanken). Aber es gibt ein paar Alternativen, die es wert sind, erkundet zu werden. Eine Wahl hängt stark von deinem Verwendungszweck ab. Ein Beispiel ist, den Ursprung von Worten, statt ganzer Worte, zu indizieren.\n\nEin Abstammungsalgorithmus ist der Prozess einer linguistischen Normalisierung, bei dem die Varianten eines Wortes in eine gleichmäßige Form reduziert werden:\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\nBeliebte Algorithmen sind der [Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) und der [Snowball stemming algorithms](https://snowballstem.org/algorithms/).\n\nEs gibt auch fortgeschrittenere Formen wie der [Lemmatisierung](https://de.wikipedia.org/wiki/Lemma_(Lexikographie)#Lemmatisierung).\n\n## Phonetische Suche\n\nEine [Phonetische Suche](https://de.wikipedia.org/wiki/Phonetische_Suche) ist ein Algorithmus, um Worte nach ihrer Aussprache zu indizieren. Anders augedrückt, erlaubt es dir Worte zu finden, die ähnlich zu den Gesuchten klingen.\n\n:::warning\nDie meisten phonetischen Algorithmen unterstützen nur eine einzige Sprache.\n:::\n\n### Soundex\n\n[Soundex](https://de.wikipedia.org/wiki/Soundex) ist ein phonetischer Algorithmus um Namen danach zu indizieren, wie sie im Englischen ausgesprochen werden. Das Ziel ist es Homophone in die gleiche Repräsentation zu übertragen, sodass sie gefunden werden, trotz der kleinen Unterschiede in der Rechtschreibung. Es ist ein unkomplizierter Algorithmus, von dem es mehrere verbesserte Versionen gibt.\n\nWenn du diesen Algorithmus verwendest, wegeben `\"Robert\"` und `\"Rupert\"` beide den String `\"R163\"`, während `\"Rubin\"` `\"R150\"` ergibt. `\"Ashcraft\"` und `\"Ashcroft\"` erzeugen beide `\"A261\"`.\n\n### Double Metaphone\n\nDer phonetische Umwandlungsalgorithmus [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) ist die zweite Generation dieses Algorithmus. Er macht mehrere fundamentale Designverbesserungen gegenüber dem originalen Metaphone-Algorithmus.\n\nDouble Metaphone klärt verschiedene Unregelmäßigkeiten im Englischen aufgrund von slawischer, germanischer, keltischer, griechischer, französischer, italienischer, spanischer, chinesischer und anderer Herkunft.\n"
  },
  {
    "path": "docs/docs/de/recipes/multi_isolate.md",
    "content": "---\ntitle: Nutzung von mehreren Isolates\n---\n\n# Nutzung von mehreren Isolates\n\nStatt in Threads, läuft der gesamte Dart-Code innerhalb von Isolates. Jeder Isolate hat einen eigenen Memory-Heap, was dafür sorgt, dass der Status eines Isolates von keinem anderen Isolate erreichbar ist.\n\nAuf Isar kann von mehreren Isolates gleichzeitig zugegriffen werden und sogar Watcher funktionieren über Isolates hinweg. In diesem Rezept werden wir prüfen, wie man Isar in einem Umfeld mit mehreren Isolates nutzt.\n\n## Wann man mehrere Isolates benutzt\n\nIsar-Transaktionen werden parallel ausgeführt, auch wenn sie im gleichen Isolate laufen. In manchen Fällen ist es dennoch von Vorteil von mehreren Isolates auf Isar zuzugreifen.\n\nDer Grund ist, dass Isar einige Zeit benötigt, um Daten von und zu Dart-Objekten zu codieren und decodieren. Du kannst es dir vorstellen, als würdest du JSON codieren und decodieren (nur effizienter). Diese Operationen laufen innerhalb des Isolates, von dem auf die Daten zugegriffen wird und blockieren daher natürlich anderen Code in dem Isolate. In anderen Worten: Isar führt einen Teil der Arbeit in deinem Dart-Isolate aus.\n\nWenn du nur ein paar hundert Objekte gleichzeitig lesen oder schreiben musst, ist es kein Problem, das im UI-Isolate zu tun. Aber für riesige Transaktionen oder wenn dein UI-Thread schon zu tun hat, solltest du überlegen ein seperates Isolate zu verwenden.\n\n## Beispiel\n\nDie erste Sache, die wir machen müssen, ist Isar in einem neuen Isolate zu öffnen. Weil eine Instanz von Isar schon im zentralen Isolate offen ist, wird `Isar.open()` diese Instanz zurückgeben.\n\n:::warning\nStelle sicher, dass du die gleichen Schemas wie im zentralen Isolate zur Verfügung stellst. Sonst wirst du einen Fehler erhalten.\n:::\n\n`compute()` startet ein neues Isolate in Flutter und führt die angegebene Funktion in ihm aus.\n\n```dart\nvoid main() {\n  // Isar im UI-Isolate öffnen\n  final dir = await getApplicationDocumentsDirectory();\n\n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // Auf Änderungen in der Datenbank warten\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // Startet ein neues Isolate und erzeugt 10000 Nachrichten\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // Nach einiger Zeit:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// Funktion, die im neuen Isolate ausgeführt werden soll\nFuture createDummyMessages(int count) async {\n  // Wir benötigen hier keinen Pfad, weil die Instanz schon offen ist\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // Wir benutzen synchrone Transaktionen in Isolates\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nEs gibt ein paar interessante Dinge, die in dem Beispiel von eben auffallen:\n\n- `isar.messages.watchLazy()` wird im UI-Isolate aufgerufen und wird über Änderungen durch ein anderes Isolate benachrichtigt.\n- Instanzen werden über den Namen referenziert. Der Standardname ist `default`, aber in diesem Beispiel haben wir ihn auf `myInstance` gesetzt.\n- Wir haben eine synchrone Transaktion benutzt, um die Nachrichten zu erzeugen. Unser neues Isolate zu blockieren ist kein Problem und synchrone Transaktionen sind ein bisschen schneller.\n"
  },
  {
    "path": "docs/docs/de/recipes/string_ids.md",
    "content": "---\ntitle: String-IDs\n---\n\n# String-IDs\n\nDas hier ist eine der häufigsten Anfragen, die ich erhalte, daher ist hier ein Tutorial zur Verwendung von String-IDs.\n\nIsar unterstützt String-IDs nicht nativ, was einen guten Grund hat: Integer-IDs sind viel effizienter und schneller. Besonders bei Links ist der Overhead einer String-ID zu signifikant.\n\nIch verstehe, dass du manchmal externe Daten speichern musst, die UUIDs oder andere nicht-Integer-IDs verwenden. Ich empfehle, die String-ID als Eigenschaft in deinem Objekt zu speichern und eine schnelle Hash-Implementation um 64-bit Integer zu generieren und als ID zu verwenden.\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nMit diesem Ansatz bekommst du das Beste aus beiden Welten: Effiziente Integer-IDs für Links und die Fähigkeit String-IDs zu nutzen.\n\n## Schnelle Hash-Funktion\n\nIdealerweise sollte deine Hash-Funktion eine hohe Qualität haben (du willst keine Kollisionen) und schnell sein. Ich empfehle die folgende Implementation:\n\n```dart\n/// FNV-1a 64bit Hash-Algorithmus optimiert für Dart-Strings\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nWenn du eine andere Hash-Funktion wählst, stelle sicher, dass sie einen 64-bit Integer zurückgibt und vermeide kryptographische Hash-Funktionen, weil die sehr viel langsamer sind.\n\n:::warning\nVermeide es `string.hashCode` zu verwenden, weil nicht garantiert werden kann, dass die Methode über verschiedenen Plattformen und Versionen von Dart hinweg stabil ist.\n:::\n"
  },
  {
    "path": "docs/docs/de/schema.md",
    "content": "---\ntitle: Schema\n---\n\n# Schema\n\nWenn du Isar benutzt, um deine App-Daten zu speichern, dann hast du mit Collections zu tun. Eine Collection ist wie die Tabelle einer Datenbank in der angeschlossenen Isar-Datenbank und kann nur einen einzigen Typen von Dart Objekt enthalten. Jedes Collection-Objekt repräsentiert eine Zeile mit Daten in der zugehörigen Collection.\n\nDie Definition einer Collection wird \"Schema\" genannt. Der Isar-Generator macht die meiste Arbeit für dich und generiert den Großteil des Codes den du benötigst, um die Collection zu benutzen.\n\n## Aufbau einer Collection\n\nJede Collection in Isar wird über die Annotation `@collection` oder `@Collection()` an einer Klasse definiert. Eine Isar-Collection enthält Felder für jede Spalte in der zugehörigen Tabelle der Datenbank, auch eine, die dem Primärschlüssel entspricht.\n\nDer folgende Code ist ein Beispiel einer simplen Collection, welche eine `User`-Tabelle mit den Spalten ID, Vorname und Nachname definiert:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nUm ein Feld persistent zu machen, muss Isar Zugriff auf das Feld haben. Du kannst sicherstellen, dass Isar Zugriff auf ein Feld hat, indem du es öffentlich machst, oder indem du Getter- und Setter-Methoden definierst.\n:::\n\nEs gibt ein paar optionale Parameter, um die Collection anzupassen:\n\n| Konfiguration | Beschreibung                                                                                                                        |\n| ------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Steuert, ob Felder von Elternklassen und Mixins in Isar gespeichert werden. Standardmäßig aktiviert.                                |\n| `accessor`    | Erlaubt dir den Standardnamen des Collection-Accessors umzubenennen (zum Beispiel zu `isar.contacts` für die `Contact`-Collection). |\n| `ignore`      | Erlaubt es bestimmte Eigenschaften zu ignorieren. Diese werden auch bei Superklassen angewendet.                                    |\n\n### Isar ID\n\nJede Collection-Klasse muss eine ID-Eigenschaft vom Typen `Id` definieren, die ein Objekt eindeutig identifiziert. `Id` ist eigentlich nur ein Alias für `int`, der es dem Isar Generator ermöglicht die ID-Eigenschaft zu erkennen.\n\nIsar indiziert ID Felder automatisch, was dir ermöglicht Objekte effizient anhand ihrer ID zu erhalten und modifizieren.\n\nDu kannst eintweder IDs selbst zuweisen oder Isar fragen eine sich automatisch erhöhende ID festzulegen. Wenn das `id`-Feld `null` und nicht `final` ist, wird Isar eine auto-increment ID setzen. Wenn du eine nicht null-bare auto-increment ID haben möchtest, kannst du `Isar.autoIncrement` anstatt von `null` verwenden.\n\n:::tip\nAuto-increment IDs werden nicht wiederverwendet, wenn ein Objekt gelöscht wird. Der einzige Weg auto-increment IDs zurückzusetzen ist die Datenbank zu leeren.\n:::\n\n### Collections und Felder umbenennen\n\nIsar benutzt standardmäßig den Klassennamen als Collectionnamen. Genauso verwendet Isar in der Datenbank Feldnamen als Spaltennamen. Wenn du willst, dass eine Collection oder ein Feld einen anderen Namen hat, dann füge die Annotation `@Name` hinzu. Das folgende Beispiel demonstriert angepasste Namen für Collections und Felder:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nDu solltest besonders dann, wenn du Dart-Felder oder -Klassen umbenennen willst, überlegen, die `@Name`-Annotation zu verwenden. Sonst wird die Datenbank das Feld oder die Collection löschen und neu erzeugen.\n\n### Felder ignorieren\n\nIsar stell sicher, dass alle öffentlichen Felder einer Collecion-Klasse erhalten bleiben. Wenn du eine Eigenschaft oder einen Getter mit `@ignore` annotierst, kannst du diese von der Sicherstellung ausschließen, wie im folgenden Code-Schnipsel gezeigt:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nIn Fällen, in denen deine Collection Felder von der Eltern-Collection erhält, ist es meist leichter die `ignore`-Eigenschaft der `@Collection`-Annotation zu verwenden:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nWenn eine Collection ein Feld mit einem Typen enthält, der nicht von Isar unterstützt wird, musst du das Feld ignorieren.\n\n:::warning\nBeachte, dass es keine gute Vorgehensweise ist, Informationen in Isar-Objekten zu speichern, die nicht gesichert werden.\n:::\n\n## Unterstützte Typen\n\nIsar unterstützt die folgenden Datentypen:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nZusätzlich sind eingebettete Objekte und Enums unterstützt. Wir behandeln diese weiter unten.\n\n## byte, short, float\n\nIn vielen Fällen benötigst du nicht den gesamten Bereich eines 64-bit Integers oder Doubles. Isar unterstützt zusätzliche Typen, die es dir erlauben Speicherpaltz beim Speichern kleinerer Zahlen zu sparen.\n\n| Typ        | Größe in bytes | Bereich                                                  |\n| ---------- | -------------- | -------------------------------------------------------- |\n| **byte**   | 1              | 0 bis 255                                                |\n| **short**  | 4              | -2.147.483.647 bis 2.147.483.647                         |\n| **int**    | 8              | -9.223.372.036.854.775.807 bis 9.223.372.036.854.775.807 |\n| **float**  | 4              | -3,4e38 bis 3,4e38                                       |\n| **double** | 8              | -1,7e308 bis 1,7e308                                     |\n\nDie zusätzlichen Zahl-Typen sind nur Aliase für die nativen Dart-Typen, also beispielsweise `short` zu benutzen funktioniert genau, wie wenn du `int` nutzen würdest.\n\nHier ist eine Beispiel-Collection, welche alle der eben genannten Typen enthält:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nAlle Zahlen-Typen können auch in Listen verwendet werden. Um Bytes zu speichern solltest du `List<byte>` benutzen.\n\n## Null-bare Typen\n\nZu verstehen wie Null-barkeit in Isar funktioniert ist essentiell: Zahlen-Typen haben **KEINE** gemeinsame festgelegte `null`-Darstellung. Stattdessen wird ein bestimmter Wert genutzt:\n\n| Typ        | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    | `int.MIN`     |\n| **float**  | `double.NaN`  |\n| **double** | `double.NaN`  |\n\n`bool`, `String`, und `List` haben eine seperate `null`-Darstellung.\n\nDieses Verhalten erlaubt Leistungsverbesserungen, und ermöglicht es die Null-barkeit deiner Felder frei zu ändern, ohne eine Migration oder speziellen Code zum handhaben von `null`-Werten zu benötigen.\n\n:::warning\nDer `byte`-Typ unterstützt keine Null-Werte.\n:::\n\n## DateTime\n\nIsar speichert keine Zeitzoneninformationen von deinen Daten. Stattdessen wandelt es `DateTime`s zu UTC um, bervor es diese speichert. Isar gibt jedes Datum in lokaler Zeit zurück.\n\n`DateTime`s werden mit Mikrosekunden-Präzision gespeichert. In Browsern ist, aufgrund von JavaScript-Limitationen, nur Millisekunden-Präzision möglich.\n\n## Enum\n\nIsar ermöglicht es Enums wie andere Isar-Typen zu nutzen und zu speichern. Du musst aber wählen, wie Isar den Enum auf dem Datenträger abbilden soll. Isar unterstützt vier verschiedene Strategien:\n\n| Enum-Typ     | Beschreibung                                                                                                   |\n| ------------ | -------------------------------------------------------------------------------------------------------------- |\n| `ordinal`    | Der Index des Enums wird als `byte` gespeichert. Das ist sehr effizienzt, aber erlaubt keine Null-baren Enums. |\n| `ordinal32`  | Der Index des Enums wird als `short` (4-Byte-Integer) gespeichert. Erlaubt keine Null-baren Enums.             |\n| `name`       | Der Name des Enums wird als `String` gespeichert.                                                              |\n| `value`      | Eine angepasste Eigenschaft wird genutzt, um den Enum-Wert abzurufen.                                          |\n\n:::warning\n`ordinal` und `ordinal32` basieren auf der Reihenfolge der Enum-Werte. Wenn du die Reihenfolge änderst, werden existierende Datenbanken falsche Werte zurückgeben.\n:::\n\nSchauen wir uns ein Beispiel für jede Strategie an.\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // entspricht EnumType.ordinal\n  late TestEnum byteIndex; // ist nicht Null-bar\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // ist nicht Null-bar\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nNatürlich können Enums auch in Listen benutzt werden.\n\n## Eingebettete Objekte\n\nEs ist oft hilfreich verschachtelte Objekte in deinem Collection-Modell zu haben. Daher gibt es keine Begrenzung, wie tief die Verschachtelung von Objekten sein kann. Beachte jedoch, dass der gesamte Objekt-Baum in die Datenbank geschrieben werden muss, um ein sehr tief verschachteltes Objekt zu aktualisieren.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nEingebettete Objekte können Null-bar sein und andere Objekte erweitern. Die einzige Voraussetzung ist, dass sie mit `@embedded` annotiert werden und einen Standardkonstruktor ohne erforderliche Parameter haben.\n"
  },
  {
    "path": "docs/docs/de/transactions.md",
    "content": "---\ntitle: Transaktionen\n---\n\n# Transaktionen\n\nIn Isar verbinden Transaktionen mehrere Datenbankoperationen in einen einzigen Arbeitsvorgang. Die meisten Interaktionen mit Isar nutzen implizit Transaktionen. Lese- & Schreibzugriff ist in Isar [ACID](https://de.wikipedia.org/wiki/ACID)-konform. Transaktionen werden automatisch zurückgesetzt, wenn ein Fehler auftritt.\n\n## Explizite Transaktionen\n\nIn einer expliziten Transaktion kannst du einen konsistenten Schnappschuss der Datenbank erhalten. Versuche die Dauer einer Transaktion zu minimieren. Es ist verboten Netzwerkabfragen oder andere lang andauernde Operationen in einer Transaktion zu machen.\n\nTransaktionen (besonders Schreib-Transaktionen) sind sehr teuer. Du solltest immer versuchen aufeinander folgende Operationen in eine einzelne Transaktion zu vereinen.\n\nTransaktionen können entweder synchron oder asynchron sein. In synchronen Transaktionen kannst du nur synchrone Operationen verwenden. In asynchronen Transaktionen sind nur asynchrone Operationen möglich.\n\n|           | Lesen        | Lesen & Schreiben |\n| --------- | ------------ | ----------------- |\n| Synchron  | `.txnSync()` | `.writeTxnSync()` |\n| Asynchron | `.txn()`     | `.writeTxn()`     |\n\n### Lese-Transaktionen\n\nExplizite Lese-Transaktionen sind optional, aber sie erlauben es atomare Lesevorgänge durchzuführen und auf einem konsistenten Status der Datenbank innerhalb der Transaktion zu arbeiten. Intern nutzt Isar für alle Lese-Operationen immer Lese-Transaktionen.\n\n:::tip\nAsynchrone Lese-Transaktionen laufen parallel zu anderen Lese- und Schreib-Transaktionen. Ziemlich cool, oder?\n:::\n\n### Schreib-Transaktionen\n\nAnders als Lese-Operationen müssen Schreib-Operationen in Isar in einer expliziten Transaktion ausgeführt werden.\n\nWenn eine Schreib-Transaktion erfolgreich beendet wird, wird sie automatisch festgesetzt und alle Änderungen werden auf den Datenträger geschrieben. Wenn ein Fehler auftritt, wird die Transaktion abgebrochen und alle Änderungen werden zurückgesetzt. Transaktionen sind „Alles oder Nichts”: Entweder sind alle Schreibvorgänge in der Transaktion erfolgreich oder keine von ihnen findet statt. Somit ist sichergestellt, dass die Daten konsistent sind.\n\n:::warning\nWenn eine Datenbankoperation fehlschlägt, wird die Transaktion abgeborchen und darf nicht mehr verwendet werden, auch wenn der Fehler in Dart aufgefangen wird.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GUT\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// SCHLECHT: Bewege die Schleife in die Transaktion\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/de/tutorials/quickstart.md",
    "content": "---\ntitle: Schnellstart\n---\n\n# Schnellstart\n\nHolla, die Waldfee! Du bist bestimmt hier um mit der coolsten Flutter-Datenbank zu starten...\n\nDieser Schnellstart wird wenig um den heißen Brei herumreden und direkt mit dem Coden beginnen.\n\n## 1. Abhängigkeiten hinzufügen\n\nBevor es losgeht, müssen wir ein paar Pakete zur `pubspec.yaml` hinzufügen. Damit es schneller geht lassen wir pub das für uns erledigen.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. Klassen annotieren\n\nAnnotiere deine Collection-Klassen mit `@collection` und wähle ein `Id`-Feld.\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // Für auto-increment kannst du auch id = null zuweisen \n\n  String? name;\n\n  int? age;\n}\n```\n\nIDs identifizieren Objekte in einer Collection eindeutig und erlauben es dir, sie später wiederzufinden.\n\n## 3. Code-Generator ausführen\n\nFühre den folgenden Befehl aus, um den `build_runner` zu starten:\n\n```\ndart run build_runner build\n```\n\nWenn du Flutter verwendest:\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Isar-Instanz öffnen\n\nÖffne eine neue Isar-Instanz und übergebe alle Collection-Schemata. Optional kannst du einen Instanznamen und ein Verzeichnis angeben.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. Schreiben und lesen\n\nWenn deine Instanz geöffnet ist, hast du Zugriff auf die Collections.\n\nAlle grundlegenden CRUD-Operationen sind über die `IsarCollection` verfügbar .\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // Einfügen & akualisieren\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // Erhalten\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // Löschen\n});\n```\n\n## Weitere Ressourcen\n\nDu lernst am besten visuell? Schau dir diese Videos an, um mit Isar zu starten:\n\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/de/watchers.md",
    "content": "---\ntitle: Watcher\n---\n\n# Watcher\n\nIsar ermöglicht es dir zu Änderungen in der Datenbank zu abbonieren. Du kannst Änderungen in einem Objekt, einer ganzen Collection oder einer Abfrage \"beobachten\".\n\nWatcher erlauben es dir auf Änderungen in der Datenbank effizient zu reagieren. Du kannst z.B. dein UI neuladen, wenn ein Kontakt hinzugefügt wurde, eine Netzwerkabfrage machen, wenn ein Dokument aktualisiert wurde, etc.\n\nEin Watcher wird benachrichtigt, wenn eine Transaktion efolgreich stattfindet, und das Ziel sich wirklich ändert.\n\n## Objekte beobachten\n\nWenn du benachrichtigt werden möchtest, wenn ein spezifisches Objekt erstellt, aktualisiert oder gelöscht wird, solltest du ein Objekt beobachten:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// Ausgabe: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// Ausgabe: User changed: Mark\n\nawait isar.users.delete(5);\n// Ausgabe: User changed: null\n```\n\nWie du im eben gezeigten Beispiel sehen kannst, muss das Objekt noch nicht existieren. Der Watcher wird benachrichtigt, wenn es erzeugt wird.\n\nEs gibt den zusätzlichen Parameter `fireImmediately`. Wenn du ihn auf `true` gesetzt hast, wird Isar sofort die Werte des aktuellen Objekts in den Stream geben.\n\n### Lazy Beobachtung\n\nVielleicht möchtest du gar nicht den neuen Wert erhalten, sondern nur über die Änderungen informiert werden. Das erspart es Isar die Objekte abrufen zu müssen:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// Ausgabe: User 5 changed\n```\n\n## Collections beobachten\n\nStatt ein einzelnes Objekt zu beobachten kannst du auch eine ganze Collection beobachten und benachrichtigt werden, wenn irgendein Objekt hinzugefügt, geändert oder gelöscht wird:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// Ausgabe: A User changed\n```\n\n## Abfragen beobachten\n\nEs ist sogar möglich ganze Abfragen zu beobachten. Isar versucht sein Bestes dich nur zu benachrichtigen, wenn das Abfrageergebnis sich wirklich ändert. Du wirst nicht informiert, wenn Links darin resultieren, dass deine Abfrageergebnisse sich ändern. Benutze einen Collection-Watcher, wenn du über Linkänderungen benachrichtigt werden willst.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// Ausgabe: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// Ausgabe: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// keine Ausgabe\n\nawait isar.users.put(User()..name = 'Antonia');\n// Ausgabe: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nWenn du einen Offset mit Limitierung oder Eindeutigkeitsabfragen benutzt, wird Isar dich auch informieren, wenn Ergebnisse innerhalb der Abfrage, aber außerhalb der Abfragegrenzen stattfinden.\n:::\n\nGenau wie bei `watchObject()` kannst du `watchLazy()` verwenden, um über Änderungen in den Abfrageergebnissen benachrichtigt zu werden, ohne die Ergebnisse zu erhalten.\n\n:::danger\nAbfragen für jede Änderung erneut ablaufen zu lassen ist sehr ineffizient. Es wäre besser, wenn du stattdessen einen lazy Collection-Watcher verwenden würdest.\n:::\n"
  },
  {
    "path": "docs/docs/es/README.md",
    "content": "---\nhome: true\ntitle: Home\nheroImage: /isar.svg\nactions:\n  - text: Empecemos!\n    link: /es/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Hecho para Flutter\n    details: Mínima inicialización, fácil de usar, sin configuración, sin repetición. Solo agrega algunas líneas de código para comenzar.\n  - title: 🚀 Altamente escalable\n    details: Almacena cientos de miles de registros en una sola base de datos NoSQL y consúltalos de forma eficiente y asíncrona.\n  - title: 🍭 Múltiples características\n    details: Isar posee una gran cantidad de características para ayudarte a administrar tus datos. Índices compuestos y multi-entrada, modificadores de consultas, soporte para JSON, y mucho más.\n  - title: 🔎 Búsqueda de texto completo\n    details: Isar tiene incorporado un buscador de texto completo. Crea un índice multi-entrada y busca texto de forma fácil.\n  - title: 🧪 Semántica ACID\n    details: Isar es compatible con ACID y maneja las transacciones automáticamente. Retrocede los cambios en caso de error.\n  - title: 💃 Tipeado estático\n    details: Las consultas de Isar son tipeadas estáticamente y verificadas en tiempo de compilación. No hay necesidad de preocuparse por errores en tiempo de ejecución.\n  - title: 📱 Multiplataforma\n    details: Soporte completo para iOS, Android, Desktop, WEB!\n  - title: ⏱ Asíncrona\n    details: Isar incluye operaciones de consulta en paralelo y soporte multi-isolate.\n  - title: 🦄 Código abierto\n    details: Completamente de código abierto y libre para siempre!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/es/crud.md",
    "content": "---\ntitle: Crear, Leer, Actualizar, Eliminar\n---\n\n# Crear, Leer, Actualizar, Eliminar (CRUD)\n\nCuando ya has definido tus colecciones, aprende cómo manipularlas!\n\n## Abriendo Isar\n\nAntes de hacer nada, necesitamos una instancia Isar. Cada instancia requiere un directorio con permisos de escritura donde el archivo de la base de datos pueda ser almacenado. Si no defines un directorio, Isar encontrará un directorio por defecto apropiado para la plataforma en uso.\n\nProvee todos los esquemas que quieras usar con la instancia Isar. Si abres múltiples instancias, aún tienes que proveer todos los esquemas a cada instancia.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [ContactSchema],\n  directory: dir.path,\n);\n```\n\nPuedes usar la configuración por defecto o proveer algunos de los siguientes parámetros:\n\n| Configuración       | Descripción                                                                                                                                                                                                                     |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `name`              | Abre múltiples instancias con distinto nombre. `\"default\"` es el nomre usado por defecto.                                                                                                                                       |\n| `directory`         | La ubicación de almacenamiento para esta instancia. Puedes usar una ruta relativa o absoluta. `NSDocumentDirectory` para iOS y `getDataDirectory` para Android son los usados por defecto. No se requiere para web.             |\n| `relaxedDurability` | Relaja la garantía de durabilidad para incrementar el rendimiento de escritura. En caso de falla del sistema (no de la aplicación), es posible perder la última transacción ejecutada. La corrupción de los datos no es posible |\n| `compactOnLaunch`   | Condiciones a verificar cuando la base de datos deba ser compactada cuando se abra la instancia.                                                                                                                                |\n| `inspector`         | Habilita el inspector para las compilaciones de depuración. Esta opción se ignora para las compilaciones de perfil y entrega.                                                                                                   |\n\nSi existiera una instancia ya abierta al momento de llamar a `Isar.open()`, ésta retornará la instancia existente independientemente de los parámetros especificados. Ésto es útil para usar Isar en un isolate.\n\n:::tip\nConsidera usar el paquete [path_provider](https://pub.dev/packages/path_provider) para obtener una ruta válida en todas las plataformas.\n:::\n\nLa ubicación de almacenamiento del archivo de la base de datos Isar es `directory/name.isar`\n\n## Leyendo la base de datos\n\nUsa instancias `IsarCollection` para buscar, consultar y crear nuevos objetos de un tipo dado en Isar.\n\nPara los ejemplos siguientes, asumimos que tenemos una colección `Recipe` definida como sigue:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### Obtener una colección\n\nTodas tus colecciones viven en la instancia Isar. Puedes obtener tu colección Recipes con:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\nEso fue fácil! Si no quieres usar los accesores de la colección, puedes usar el método `collection()`:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### Obtener un objeto (por su id)\n\nTodavía no tenemos datos en la colección, pero pretendamos que tenemos así podemos obtener un objeto imaginario dado su id `123`\n\n```dart\nfinal recipe = await recipes.get(123);\n```\n\n`get()` retorna un `Future` con el objeto o `null` si éste no existe. Por defecto todas las operaciones Isar son asíncronas, y la mayoría de ellas tienen su versión síncrona:\n\n```dart\nfinal recipe = recipes.getSync(123);\n```\n\n:::warning\nEn tus isolate de UI, por defecto deberías usar los métodos en su versión asíncrona. Debido a que Isar es súper rápido, a menudo es aceptable usar la versión síncrona.\n:::\n\nSi quieres obtener múltiples objetos de una vez, utiliza `getAll()` o `getAllSync()`:\n\n```dart\nfinal recipe = await recipes.getAll([1, 2]);\n```\n\n### Consulta de objectos\n\nEn lugar de obtener objetos por su id, puedes también consultar una lista objetos que coincidan con ciertas condiciones usando `.where()` y `.filter()`:\n\n```dart\nfinal allRecipes = await recipes.where().findAll();\n\nfinal favouires = await recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ Ver más en: [Consultas](queries)\n\n## Modificando los datos\n\nFinalmente es momento de modificar los datos en nuestra colección! Para crear, actualizar o eliminar objectos, usa las respectivas operaciones juntas en una transacción de escritura:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await recipes.get(123)\n\n  recipe.isFavorite = false;\n  await recipes.put(recipe); // perform update operations\n\n  await recipes.delete(123); // or delete operations\n});\n```\n\n➡️ Ver más en: [Transacciones](transactions)\n\n### Insertar objectos\n\nPara almacenar un objeto en Isar, insértalo en una colección. El método `put()` de Isar insertará o actualizará el objecto dependiendo si el mismo ya existe o no en la colección.\n\nSi el campo id es `null` o `Isar.autoIncrement`, Isar usará un id auto incrementable.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await recipes.put(pancakes);\n})\n```\n\nIsar asignará automáticamente el id al objeto si el campo id es no-final.\n\nInsertar múltiples objetos de una sola vez es muy fácil:\n\n```dart\nawait isar.writeTxn(() async {\n  await recipes.putAll([pancakes, pizza]);\n})\n```\n\n### Actualizar objectos\n\nCrear y actualizar objetos funcionan ambos con `collection.put(object)`. Si el id es `null` (o no existe), el object se crea; de otra manera será actualizado.\n\nEntonces si queremos quitar los pancakes de los favoritos, podemos hacer los siguiente:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await recipes.put(recipe);\n});\n```\n\n### Eliminar objetos\n\nQuieres eliminar un objeto en Isar? Usa `collection.delete(id)`. El método delete retorna verdadero si el objeto con el id especificado fue encontrado y eliminado. Por ejemplo, si quieres eliminar el objeto con el id `123`, puedes hacer:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nDe manera similar a get y put, también existe una operación para eliminar múltiples objetos de una vez que retorna la cantidad de objetos eliminados:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\nSi no conoces los ids de los objetos que quieres eliminar, puedes utilizar una consulta:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/es/faq.md",
    "content": "---\ntitle: FAQ\n---\n\n# Preguntas frecuentes\n\nUna colección aleatoria de preguntas frecuentes sobre Isar y bases de datos en Flutter.\n\n### Porqué necesito una base de datos?\n\n> Estoy almacenando mis datos es una base datos en mi backend, porqué necesito Isar?.\n\nIncluso hoy en día, es muy común no tener conexión a internet si estás en el subterráneo o en un avión o si visitaste a tu abuela, que no tiene WiFi y muy mala señal de celular. No deberías dejar que la mala conexión afectua a tu aplicación!\n\n### Isar versus Hive\n\nLa respuesta es fácil: Isar [inició como un reemplazo para Hive](https://github.com/hivedb/hive/issues/246) y está en un estado de madurez tal que se recomienda siempre usar Isar en lugar de Hive.\n\n### Cláusulas `where`?!\n\n> Porqué **_YO_** tengo que elejir qué índice usar?\n\nExisten muchas razones. Muchas base de datos utilizan heurística para elegir el mejor índice para una determinada consulta. La base de datos necesita recolectar datos de uso adicionales (-> overhead) y aún así podría elegir un índice incorrecto. Además crear la consulta es más lento.\n\nNadie conoce tus datos mejor que tú, el desarrollador. Entonces tú puedes elegir el índice óptimo y decidir por ejemplo si quieres usar un índice para consultas u ordenamiento.\n\n### Tengo que usar índices / cláusulas `where`?\n\nNo! Isar es lo suficientemente rápida si solo quieres confiar en filtros.\n\n### Isar es lo suficientemente rápida?\n\nIsar está entre las bases de datos más rápidas para dispositivos móbiles, por lo que debería ser lo suficientemente rápida para las mayoría de los casos de uso. Si tienes problemas de rendimiento, hay posibilidades que estés haciendo algo mal.\n\n### Isar incrementa el tamaño de mi aplicación?\n\nUn poco, sí. Isar incrementará el tamaño de descarga de tu aplicaicón alrededor de 1 - 1.5 MB. Isar Web agrega solo algunos KB.\n\n### La documentación es incorrecta / hay un error de ortografía.\n\nOh no, lo siento. Por favor [apunta el problema](https://github.com/isar-community/isar/issues/new/choose) o, mejor aún, un PR para solucionarlo! 💪.\n"
  },
  {
    "path": "docs/docs/es/indexes.md",
    "content": "---\ntitle: Índices\n---\n\n# Índices\n\nLos índices son la característica más poderosa de Isar. Muchas bases de datos embebidas ofrecen índices \"normales\" (o nada), pero Isar también tiene índices compuestos y multi-entrada. Entender cómo funcionan los índices es esencial para optimizar el rendimiento de las consultas. Isar te permite elegir qué índice quieres usar y cómo quieres usarlo. Comenzaremos con un inicio rápido sobre qué son los índices.\n\n## Qué son los índices?\n\nCuando una colección no está indexada, el orden de las filas no será identificable por la consulta como optimizada en ninguna forma, y tu consulta tendrá que buscar entonces a través de todos los objectos de forma lineal. En otras palabras, la consulta deberá buscar a través de cada objeto para encontrar los que coincidan con las condiciones. Como puedes imaginarte, eso puede tardar mucho. Buscar a través de cada objeto no es muy eficiente.\n\nPor ejemplo, esta colección `Product` está completamente desordenada.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**Datos:**\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nUna consulta que intente buscar todos los productos que cuestan más de $30 tiene que buscar a través de todas las nueve filas. No es un problema para nueve filas, pero podría ser un problema para 100k filas.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nPara mejorar el rendimiento de esta consulta, indexamos la propiedad `price`. Un índice es como una tabla de búsqueda ordenada:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**Índices generados:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nAhora, la ejecución de la consulta puede ser considerablemente más rápida. El ejecutor puede saltar directamente a los últimos 3 índices y buscar los objetos correspondientes por su id.\n\n### Ordenando\n\nOtra cosa genial: los índices permiten ordenar súper rápido. Las consultas ordenadas son costosas porque la base de datos tiene que cargar todos los resultados en memoria antes de ordenarlos. Incluso si especificaste un offset y un límite, éstos se aplican después de ordenar.\n\nImaginemos que queremos encontrar los cuatro productos más baratos. Podríamos usar la siguiente consulta:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nEn este ejemplo, la base de datos tendría que cargar todos los objetos (!), ordenarlos por precio, y retornar los 4 productos con el menor precio.\n\nComo puedes imaginar, ésto puede hacerse mucho más eficiente usando el índice anterior. La base de datos toma las cuatro primeras filas del índice y retorna los objetos correspondientes ya que éstos ya están en el orden correcto.\n\nPara usar el índice para ordenar, escribiríamos la consulta como sigue:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nLa cláusula `where` `.anyX()` le dice a Isar que use un ídice sólo para ordenar. También puedes usar una cláusula `where` como `.priceGreaterThan()` y obtener los resultados ordenados.\n\n## Índices únicos\n\nUn índice único asegura que el índice no contiene valores duplicados. Puede consistir en una o múltiples propiedades. Si un índice único tiene una propiedad, los valores en esta propiedad serán únicos. Si el índice único tiene más de una pro[iedad, la combinación de los valores en estas propiedades es única.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nCualquier intento de insertar o actualizar datos en un índice único que provoque un duplicado resultará en un error:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// try to insert user with same username\nawait isar.users.put(user2); // -> error: unique constraint violated\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## Índices con reemplazo\n\nA veces no es deseable arrojar un error si una condición de único es violada. En su lugar, podrías querer reemplazar el objeto existente con el nuevo. Ésto se puede lograr estableciendo la propiedad `replace` del índice a `true`.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nAhora cuando querramos insertar un usuario con nombre de usuario existente, Isar reemplazará el usuario existente con el nuevo.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nLos índices con reemplazo también generan métodos `putBy()` que permiten actualizar los objetos en lugar de reemplazarlos. El id existente es reusado, **_and links are still populated_**.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// user does not exist so this is the same as put()\nawait isar.users.putByUsername(user1);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nComo puedes ver, el id del primer usuario insertado es reusado.\n\n## Índices mayúsculas-minúsculas\n\nTodos los índices en las propiedades `String` y `List<String>` por defecto distinguen entre mayúsculas y minúsculas. Si quieres que tu índice no haga esta distinción, puedes usar la opción `caseSensitive`:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## Tipos de índices\n\nExisten diferentes tipos de índices. La mayoría del tiempo, querrás usar un índice tipo `IndexType.value`, pero los índices hash son más eficientes.\n\n### Índice valor\n\nEl índice valor es el tipo por defecto y el único posible para todas las propiedades que no sean se tipo String o List. Para construir el índice se utilizan los valores de las propiedades. En el caso de las listas, se utilizan sus elementos. De los tres tipos de índices disponibles, es el más flexible como así también el que más espacio utiliza.\n\n:::tip\nUsa `IndexType.value` para primitivas, Strings donde necesites una cláusula `startsWith()`, y listas si quieres buscar por elementos individuales.\n:::\n\n### Índice hash\n\nLos strings y las listas pueden reducirse para disminuir significativamente el espacio en disco que requiere el índice. La desventaja es que no puede usarse para búsqueda por prefijo (cláusulas `startsWith`).\n\n:::tip\nUsa `IndexType.hash` para strings y listas si no necesitas utilizar cláusulas `startsWith` ni `elementEqualTo`.\n:::\n\n### Índice hashElements\n\nLas listas de string pueden reducirse como un todo (usando `IndexType.hash`), o los elementos de la lista pueden reducirse individualmente (usando `IndexType.hashElements`), creando un índice multi-entrada con los elementos reducidos.\n\n:::tip\nUsa `IndexType.hashElements` para `List<String>` sin nevesitas aplicar cláusulas `elementEqualTo`.\n:::\n\n## Índices compuestos\n\nUn índice compuesto es un índice con múltiples propiedades. Isar te permite crear índices compuestos de hasta tres propiedades.\n\nLos índices compuestos también son conocidos como índices multi-columna.\n\nProbablemente sea mejor comenzar con un ejemplo. Creamos una colleción person y definimos un índice compuesto en las propiedades age y name:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**Datos:**\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**Índice generado:**\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nEl índice compuesto generado contiene a todas las personas ordenadas por su edad y su nombre.\n\nLos índices compuestos son geniales si necesitas crear consultas eficientes ordenadas por propiedades múltiples. También te pemiten utilizar cláusulas `where` avanzadas:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nLa última propiedad del índice compuesto también soporta condiciones como `startsWith()` o `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Índices multi-entrada\n\nSi indexas una lista usando `IndexType.value`, Isar automáticamente creará un índice multi-entrada, y cada elemento en la lista será indexado hacia el objeto, Funciona para cualquier tipo de lista.\n\nAplicaciones prácticas del uso de índices multi-entrada incluyen indexar una lista de etiquetas o crear un índice de texto completo.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` divide la cadena en palabras de acuerdo con la especificación [Unicode Annex #29](https://unicode.org/reports/tr29/), por lo tanto funciona correctamente para cualquier idioma.\n\n**Data:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nEntradas con palabras duplicadas paraecen sólo una vez en el índice.\n\n**Índice generado:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nEste índice ahora puede usarse para cláusulas por prefijo (o igualdad) de las palabras individuales de la descripción.\n\n:::tip\nEn lugar de guardar las palabaras directamente, considera usar los resultados de un [algoritmo de fonética](https://en.wikipedia.org/wiki/Phonetic_algorithm) como [Soundex](https://es.wikipedia.org/wiki/Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/es/limitations.md",
    "content": "# Limitaciones\n\nComo ya sabes, Isar funciona en dispositivos móbiles y de escritorio corriendo en la VM así como en la web. Ambas plataformas son muy diferentes y tienen distintas limitaciones.\n\n## Limitaciones de la VM\n\n- Para consultas `where` de prefijo sólo se pueden usar los primeros 1024 bytes\n- Los objetos pueden ser de 16MB en tamaño como máximo\n\n## Limitaciones de la Web\n\nDado que Isar Web confía en IndexedDB, hay más limitaciones pero apenas son notadas mientras se usa Isar.\n\n- No hay soporte para métodos síncronos\n- Actualmente, los filtros `Isar.splitWords()` y `.matches()` aún no están implementados\n- Los cambios en los esquemas no son estrechamente verificados como en la VM entonces sé cuidadoso de cumplir con las reglas\n- Todos los tipos numéricos se almacenan como `double` (el único tipo numérico de js) por lo tanto `@Size32` no tiene efecto\n- Lo índices se representan de forma diferente entonces los índices hash no usan menos espacio (pero funcionan de la misma manera)\n- `col.delete()` y `col.deleteAll()` funcionan correctamente pero el valor retornado es incorrecto\n- `col.clear()` no resetea el valor de auto incrementado\n- `NaN` no está soportado como valor\n"
  },
  {
    "path": "docs/docs/es/links.md",
    "content": "---\ntitle: Enlaces\n---\n\n# Enlaces\n\nLos enlaces permiten establecer relaciones entre objetos, como ser el autor de un comentario (User). Con los enlaces de Isar, se pueden modelar relaciones `1:1`, `1:n`, y `n:n`. Usar enlaces es menos ergonómico que usar objetos embebidos y se deberían usar los últimos siempre que sea posible.\n\nPiensa en el enlace como una tabla separada que contiene la relación. Es similar a las relaciones de SQL pero tiene una API y características diferentes.\n\n## IsarLink\n\n`IsarLink<T>` puede contener uno o nigún objeto relacionado, y puede ser usado para expresar una relación a uno. `IsarLink` tiene una sola propiedad llamada `value` que contiene el objeto enlazado.\n\nLos enlaces son perezosos, entonces tienes que decirle explícitamente al `IsarLink` que cargue o guarde el valor `value`. Puedes hacer esto llamando a `linkProperty.load()` y `linkProperty.save()` respectivamente.\n\n:::tip\nLa propiedad id de las colecciones de origen y destino de un enlace deberían ser no final.\n:::\n\nEn las plataformas no web, los enlaces se cargan automáticamente cuando los usas por primera vez. Comencemos agregando un IsarLink a la colección:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nDefinimos un enlace entre maestros y estudiante. En este ejemplo, cada estudiante puede tener exactamente un maestro.\n\nPrimero, creamos el maestro y lo asignamos a un estudiante. Tendremos que insertar el maestro y guardar el enlace manualmente.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\nAhora podemos usar el enlace:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nProbemos hacer los mismo usando código síncrono. No necesitamos guardar el enlace porque `.putSync()` guarda todos los enlaces automáticamente. Incluso crea el maestro por nosotros.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nTendría más sentido si un estudiante del ejemplo anterior pudiera tener más de un maestro. Afortunadamente, Isar tiene `IsarLinks<T>`, que pueden tener múltiples objetos relacionados y expresar relaciones `to-many`.\n\n`IsarLinks<T>` extiende `Set<T>` y expone todos los métodos que están permitidos para los sets.\n\nEl comportamiento de `IsarLinks` es similar a `IsarLink` y también es perezoso. Para cargar todos los objetos enlazados se debe llamar a `linkProperty.load()`. Para guardar los cambios, llama a `linkProperty.save()`.\n\nInternamente ambos `IsarLink` y `IsarLinks` se representan de la misma forma. Podemos actualizar el `IsarLink<Teacher>` anterior a un `IsarLinks<Teacher>` para asignar múltiples maestros a un estudiante (sin perder datos).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nEsto funciona porque no cambiamos el nombre del enlace (`teacher`), entonces Isar lo recuerda de antes.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Backlinks\n\nTe escuché decir, \"Y si necesito expresar relaciones a la inversa?\". No te precupes! Te presento a los `backlinks`.\n\nLos backlinks son enlaces en la dirección inversa. Cada enlace tiene un backlink implícito. Puedes hacer que esté disponible para tu aplicación anotando un `IsarLink` o `IsarLinks` con `@Backlink()`.\n\nLos backlinks no requieren memoria o recursos adicionales; puedes agregarlos libremente, puedes borrarlos o renombrarlos sin perder datos.\n\nQueremos saber qué estudiantes tiene un maestro, entonces definimos un backlink:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nNecesitamos especificar el enlace al cual apunta el backlink. Es posible tener múltiples enlaces diferentes entre dos objetos.\n\n## Inicializar enlaces\n\nLos `IsarLink` y `IsarLinks` tienen un constructor de cero argumentos,que debería ser usado para asignar la propiedad enlace que se crea el objeto. Es buena práctica hacer que las propiedades de los enlaces sean `final`.\n\nCuando insertas (`put()`) tus objectos por primera vez, el enlace se inicializa con las collecciones origen y destino, y puedes llamar métodos como `load()` y `save()`. Un enlace comienza a serguir los cambios inmediatamente después de su creación, entonces puede agregar o quitar relaciones incluso antes que el enlace sea inicializado.\n\n:::danger\nEs ilegal mover un enlace a otro objeto.\n:::\n"
  },
  {
    "path": "docs/docs/es/queries.md",
    "content": "---\ntitle: Consultas\n---\n\n# Consultas\n\nLas consultas se utilizan para buscar registros que coincidan con ciertas condiciones, por ejemplo:\n\n- Buscar todos los contactos favoritos\n- Buscar contactos con nombre distinto\n- Borrar todos los contactos que no tengan definido un apellido\n\nDado que las consultas se ejecutan en la base de datos y no en Dart, son realmente rápidas. Si utilizas índices de manera inteligente, puedes mejorar el rendimiento de las consultas todavía más. A continuación, aprederás cómo esribir consultas y cómo lograr que sean lo más rápidas posible.\n\nExisten dos métodos diferentes para firltrar tus registros: Filtros y cláusulas where. Comenzaremos hechando un vistazo a cómo funcionan los filtros.\n\n## Filtros\n\nLos filtros son fáciles de usar y de entener. Dependiendo del tipo de tus propiedades, existen operaciones de filtrado diferentes con nombres bien definidos.\n\nLos filtros funcionan evaluando una expresión para cada objeto en la colección que está siendo filtrada. Si la expresión resuelve en verdadero, Isar incluye el objeto en los resultados. Los filtros no afectan el orden de los resultados.\n\nUsaremos el modelo siguiente para los ejemplos:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### Query conditions\n\nDependiendo del tipo de campo, tienes diferentes condiciones disponibles.\n\n| Condición                | Descripción                                                                                                                                                 |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `.equalTo(value)`        | Coincide con valores que son iguales a `value`.                                                                                                             |\n| `.between(lower, upper)` | Conicide con valores que están entre `lower` y `upper`.                                                                                                     |\n| `.greaterThan(bound)`    | Coincide con valores que son mayores que `bound`.                                                                                                           |\n| `.lessThan(bound)`       | Coincide con valores que son menores que `bound`. Los valores `null` serán incluídos por defecto ya que `null` se considera menor que cualquier otro valor. |\n| `.isNull()`              | Coincide con valores `null`.                                                                                                                                |\n| `.isNotNull()`           | Coindice con valores que no son `null`.                                                                                                                     |\n| `.length()`              | Las consultas de tamaño de listas, strings y links filtran objectos basados en el número de elementos en una lista o link.                                  |\n\nAsumeindo que la base de datos contiene cuatro zapatos de talle 39, 40, 46 y uno con talle no asignado (`null`). A menos que utilice orden de resultados, los valores serán ordenados por su id.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### Operadores lógicos\n\nPuedes encadenar expresiones usando los operadores lógicos siguientes:\n\n| Operador   | Descripción                                                                          |\n| ---------- | ------------------------------------------------------------------------------------ |\n| `.and()`   | Se evalúa como verdadero si ambas expresiones de izquierda y derecha son verdaderas. |\n| `.or()`    | Se evalúa como verdadero si alguna de las expresiones es verdadera.                  |\n| `.xor()`   | Se evalúa como verdadero si sólo una de las expresiones es verdadera.                |\n| `.not()`   | Invierte (niega) el resultado de la expresión siguiente.                             |\n| `.group()` | Agrupa condiciones y permite especificar un orden de evaluación.                     |\n\nSi quieres buscar todos los zapatos de talle 46, puedes hacer lo siguiqnte:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nSi quieres usar más de una condición, puedes combinar múltiples filtros usando los operadores **and** `.and()`, **or** `.or()` y **xor** `.xor()`.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Optional. Filters are implicitly combined with logical and.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nEsta consulta es equivalente a: `size == 46 && isUnisex == true`.\n\nTambién puedes agrupar condiciones usando `.group()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nEsta consulta es equivalente a `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nPara negar una condición o grupo, usa el operador lógico **not** `.not()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nEsta consulta es equivalente a `size != 46 && isUnisex != true`.\n\n### Condiciones sobre Strings\n\nAdicionalmente a las condiciones mencionadas anteriormente, los valores de tipo String ofrecen algunas condiciones más. Por ejemplo, los comodines para expresiones regulares permiten mayor flexibilidad en las búsquedas.\n\n| Condition            | Description                                         |\n| -------------------- | --------------------------------------------------- |\n| `.startsWith(value)` | Coincide con strings que comiencen con `value`.     |\n| `.contains(value)`   | Coincide con strings que contengan `value`.         |\n| `.endsWith(value)`   | Coincide con strings que terminen con `value`.      |\n| `.matches(wildcard)` | Coincide según la evaluación del patrón `wildcard`. |\n\n**Sensibilidad a las mayúsculas y minúsculas**  \nTodas las operaciones con strings tienen un parámetro opcional `caseSensitive` para distinguir entre mayúsculas y minúsculas que por defecto está seteado en verdadero.\n\n**Comodines:**  \nUna [expresión comodín](https://es.wikipedia.org/wiki/Car%C3%A1cter_comod%C3%ADn) es una cadena de texto (string) que utiliza caracteres normales combinados con dos caracteres especiales comodines:\n\n- El comodín `*` coincide con ninguno o más de cualquier caracter.\n- El comodín `?` coincide con un caracter cualquiera.\n  Por ejemplo, la cadena comodín `\"d?g\"` coincide con `\"dog\"`, `\"dig\"`, y `\"dug\"`, Pero no con `\"ding\"`, `\"dg\"`, o `\"a dog\"`.\n\n### Modificadores de consultas\n\nA veces es necesario construir una consulta basándose en algunas condiciones o para diferentes valores. Isar posee una herramienta muy poderosa para construir consultas condicionales:\n\n| Modificador           | Descripción                                                                                                                                                                                  |\n| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `.optional(cond, qb)` | Extiende la consulta únicamente si la `condición` es verdadera. Esto puede usarse en cualquier lugar en una consulta, por ejemplo para aplicar ordenamiento o límites de manera condicional. |\n| `.anyOf(list, qb)`    | Extiende la consulta para cada valor en `values` y combina las condiciones usando el operador lógico **or**.                                                                                 |\n| `.allOf(list, qb)`    | Extiende la consulta para cada valor en `values` y combina las condiciones usando el operador lógico **and**.                                                                                |\n\nEn este ejemplo, construiremos un método que puede buscar zapatos con un filtro opcional:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // only apply filter if sizeFilter != null\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nSi quieres buscar zapatos entre múltiples talles, puedes usar una consulta convencional o usar el modificador `anyOf()`:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nLos modificadores de consultas son especialmente útiles si quieres construir consultas dinámicas.\n\n### Listas\n\nIncluso se puede construir consultas sobre listas:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nPuedes consultar basándote en la longitud de la lista:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nÉstos son equivalenets al código Dart `tweets.where((t) => t.hashtags.isEmpty);` y `tweets.where((t) => t.hashtags.length > 5);`. También puedes consultar basándote en los elementos de la lista:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nEsto es equivalente al código Dart `tweets.where((t) => t.hashtags.contains('flutter'));`.\n\n### Objetos embebidos\n\nLos objetos embebidos son una de las funcionalidades más útiles de Isar. Se pueden consultar de manera muy eficiente usando las mismas condiciones disponibles para los objetos raíz. Asumiendo que tenemos el siguiente modelo:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nNecesitamos consultar todos los autos que sean de la marca `\"BMW\"` y del país `\"Germany\"`. Podemos hacerlo con la siguiente consulta:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nSiempre trata de agrupar las consultas anidadas. La consulta anterior es más eficiente que ésta siguiente auque el resultado es el mismo:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### Enlaces\n\nSi tus modelos contienen [links or backlinks](links) puedes filtrar tus consultas basándote en el objeto enlazado o la cantidad de objetos enlazados.\n\n:::warning\nTen en cuenta que las consultas sobre enlaces pueden ser costosas ya que Isar necesita buscar en los objetos enlazados. Considera usar objetos embebidos en su lugar.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nPodemos buscar todos los estudiantes que tienen un maestro de Matemáticas o de Inglés:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nLos filtros sobre enlaces se evalúan en verdadero si al menos unos de los objetos enlazados coincide con la condición.\n\nBusquemos todos los estudiantes que no tienen maestro:\n\n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\no:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Cláusulas `where`\n\nLas cláusulas `where` son una herramienta muy poderosa, pero puede ser algo desafiante lograr usarlas de la manera correcta.\n\nEn contraste con los filtros, las cláusulas `where` usan los índices que definiste en el esquema para verificar las condiciones de la consulta. Consultar un índice es mucho más rápido que filtrar cada registro individualmente.\n\n➡️ Ver más en: [Índices](indexes)\n\n:::tip\nComo regla básica, deberías intentar reducir la cantidad de registros lo mayor posible usando cláusulas `where` y luego hacer el filtrado restante usando filtros.\n:::\n\nSólo puedes combinar cláusulas `where` usando operaciones lógicas **or**. En otras palabras, puedes sumar múltiples cláusulas `where`, pero no puedes consultar la intersección de múltiples de ellas.\n\nAgreguemos ídices a nuestra colección shoe:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nTenemos dos índices. El índice en `size` nos permite usar cláusulas `where` como `.sizeEqualTo()`. El índice compuesto en `isUnisex` nos permite usar cláusulas como `isUnisexSizeEqualTo()`. Pero también `isUnisexEqualTo()` porque siempre puedes usar cualquier prefijo de un índice.\n\nAhora podemos reescribir la consulta anterior que busca zapatos unisex de talle 46 usando el índice compuesto. Esta consulta será mucho más rápida que la anterior:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nLas cláusulas `where` tienen dos superpoderes adicionales: Te brindad ordenado \"libre\" y una súper rápida operación `distinct`.\n\n### Combinando cláusulas `where` y filtros\n\nRecuerdas la consulta `shoes.filter()`? Es en realidad un atajo para `shoes.where().filter()`. Puedes (y deberías) combinar cláusulas `where` y filtros en la misma consulta para usar los beneficios de ambos:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nLa cláusula `where` se aplica primero para reducir el número de objetos a ser filtrados. Luego se aplica el filtro a los objetos restantes.\n\n## Ordenando\n\nPuedes definir cómo se deben ordenar los resultados cuando se ejecuta una consulta usando los métodos `.sortBy()`, `.sortByDesc()`, `.thenBy()` y `.thenByDesc()`.\n\nPara buscar todos los zapatos ordenados por nombre de modelo en orden ascendente sin usar un índice:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nOrdenar un gran número de resultados puede ser costoso, especialmente dado que el ordenamiento sucede antes que el salto y los límites. Los métodos de ordenamiento anteriores nunca hacen uso de índices. Afortunadamente, también podemos hacer ordenamiento usando cláusulas `where` y hacer que nuestra consulta sea rápida como un rayo aún si tenemos que ordenar un millón de objetos.\n\n### Ordenando con cláusulas `where`\n\nSi usas una sola cláusula `where` en tu consulta, los resultados ya están ordenados por su índice. Eso ya es mucho!\n\nSupongamos que tenemos zapatos en talle `[43, 39, 48, 40, 42, 45]` y queremos buscar todos los zapatos de talle mayor a `42` y además los queremos ordenados por talle:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // also sorts the results by size\n  .findAll(); // -> [43, 45, 48]\n```\n\nComo puedes ver, el resultado está ordenado por el índice `size`. Si quieres invertir el orden, puedes establecer `sort` a `Sort.desc`:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nEs posible que no quieras usa la cláusula `where` pero sí beneficiarte del ordenamiento implícito. Puedes usar la cláusula `any`:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nSi usas un índice compuesto, los resultados son ordenados según todos los campos en el índice.\n\n:::tip\nSi necesitas ordenar tus resultados, considera usar índices para eso. Especialmente si trabajas con `offset()` y `limit()`.\n:::\n\nA veces no es posible o no es útil usar índices para ordenar. En esos casos, usa índices para reducir el número de resultados lo más posible.\n\n## Valores únicos\n\nPara retornar sólo entradas con valores únicos, utiliza el predicado `distinct`. Por ejemplo, para saber cuántos modelos diferentes de zapatos tienes en base de datos Isar:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nTambién puedes encadenar múltiples condiciones `distinct` para buscar todos los zapatos con distinta combinación de modelo-talle:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nSólo se retorna el primer valor de cada combinación distinta. Puedes usar cláusulas `where` y operaciones de ordenamiento para controlarlos.\n\n### Cláusula `where` `distinct`\n\nSi tienes un ídice que no es único, podrías querer obtener todos sus valores distintos. Podrías usar la operación `distinctBy` de la sección anterior, pero se ejecuta después del ordenamiento y filtrado, por lo que hay algunas operaciones adicionales.\nSi solo usas una sola cláusula `where`, puedes por el contrario confiar en el índice para ejecutar la operación `distinct`.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nEn teoría, podrías incluso usar múltiples cláusulas `where` para ordenamiento y distintos. La única restricción es que aquellas cláusulas `where` no se superpongan y usen el mismo índice. Para un correcto ordenamiento, también tienen que ser aplicadas en el orden de ordenamiento. Debes ser muy cuidadoso si utilizas estos métodos!\n:::\n\n## Offset y Límite\n\nA menudo es buena idea limitar el número de resultados de una consulta para vistas de listas perezosas. Puedes hacer esto estableciendo un `limit()`:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nEstableciendo un `offset()` puedes también paginar los resultados de su consulta.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nDado que el instanciado de objetos Dart es a menudo la parte más costosa cuando se ejecuta una consulta, es una buena idea cargar sólo los objectos que necesitas.\n\n## Orden de ejecución\n\nIsar ejecuta las consultas siempre en el mismo orden:\n\n1. Atravesar índices primarios o secundarios para buscar objetos (aplicar las cláusulas `where`)\n2. Filtrar objetos\n3. Ordenar resultados\n4. Aplicar operaciones `distinct`\n5. Aplicar `offset` y `limit` a los resultados\n6. Retornar los resultados\n\n## Operaciones de consulta\n\nEn los ejemplos anteriores, usamos `.findAll()` para recuperar todas las coincidencias de objectos. Sin embargo, hay más operaciones disponibles:\n\n| Operaciones      | Descripción                                                                                                                    |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `.findFirst()`   | Recupera el primer objeto coincidente con la consulta o `null` si no se encontró ninguna.                                      |\n| `.findAll()`     | Recupera todos los objetos para la consulta.                                                                                   |\n| `.count()`       | Cuenta cuántos objetos coinciden con la consulta.                                                                              |\n| `.deleteFirst()` | Elimina de la colección el primer objeto coincidente con la consulta.                                                          |\n| `.deleteAll()`   | Elimina de la colección todos los objetos coincidentes con la consulta.                                                        |\n| `.build()`       | Compila la consulta para ser usada luego. Esto ahorra el costo de contruir una consulta si tienes que ejecutarla muchas veces. |\n\n## Consulta de propiedades\n\nSi estás interesado solamente en los valores de un propiedad simple, puedes usar consulta de propiedades. Simplemente construye una consulta regular y selecciona una propiedad:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nUsar una sola propiedad ahora tiempo durante el deserializado. Las consultas de propiedades también funcionan para los objetos embebidos y las listas.\n\n## Agregación\n\nIsar soporta el agregado de los valores de una consulta de propiedad. Las siguientes operaciones de agregación están disponibles:\n\n| Operación    | Descripción                                                           |\n| ------------ | --------------------------------------------------------------------- |\n| `.min()`     | Busca el valor mínimo o `null` si ninguno coincide.                   |\n| `.max()`     | Busca el valor máximo o `null` si ninguno coincide.                   |\n| `.sum()`     | Suma todos los valores.                                               |\n| `.average()` | Calcula el promedio de todos los valores o `NaN` si ninguno coincide. |\n\nUsar agregaciones es mucho más rápido que buscar todos los valores y realizar las operaciones de forma manual.\n\n## Consultas dinámicas\n\n:::danger\nEsta sección no debería ser relevante. El uso de consultas dinámicas está desaconsejado a menos que sea abosulamente necesario (lo cual es poco probable).\n:::\n\nTodos los ejemplos anteriores usan el QueryBuilder y los métodos estáticos generados. Podrías querer crear una consulta dinámica o un lenguaje de consultas personalizado (como el Isar Inspector). En ese caso, puedes usar el método `buildQuery()`:\n\n| Parámetro       | Descripción                                                                                        |\n| --------------- | -------------------------------------------------------------------------------------------------- |\n| `whereClauses`  | La cláusula `where` de la consulta.                                                                |\n| `whereDistinct` | Si las consultas deben retornan sólo valores distintos (solo útil para consultas `where` simples). |\n| `whereSort`     | El orden de atravesado de la cláusula `where` (solo útil para consultas `where` simples).          |\n| `filter`        | El filtro a aplicar al resultado.                                                                  |\n| `sortBy`        | Una lista de propiedades para definir el orden del resultado.                                      |\n| `distinctBy`    | Una lista de propiedades para aplicar `distinct`.                                                  |\n| `offset`        | El offset de los resultados.                                                                       |\n| `limit`         | El número máximo de resultados a retornar.                                                         |\n| `property`      | Si no es null, sólo se retornan los valores de ésta propiedad.                                     |\n\nCreemos una consulta dinámica:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nLa siguiente consulta es equivalente:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/es/recipes/data_migration.md",
    "content": "---\ntitle: Migración de datos\n---\n\n# Migración de datos\n\nIsar migra automáticamente tus esquemas de la base de datos si agregas o quitas colecciones, campos o índices. Probablemente quieras migrar también tus datos. Isar no ofrece una solución incluída porque impondría restricciones arbitrarias a la migración. Es sencillo implementar una lógica de migración que se adecúe a tus necesidades.\n\nEn este ejemplo usaremos una versión simple de la base de datos completa. Utilizamos `SharedPreferences` para almacenar la versión actual y compararla con la versión a la cual queremos migrar. Si la versión no coincide, migramos los datos y actualizamos la versión.\n\n:::tip\nTambién podrías darle a cada colección su propia versión y migrarlas individualmente.\n:::\n\nImagina que tenemos una colección de usuarios con un campo de cumpleaños. En la versión 2 de nuetra app, necesitamos agregar un campo adicional para el año de nacimiento para consultar usuarios por edad.\n\nVersion 1:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersion 2:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\nEl problema es que el modelo existente para los usuarios tendrá un campo vacío `birthYear` porque no existía en la versión 1. Necesitamos migrar los datos para establecer el campo `birthYear`.\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // If the version is not set (new installation) or already 2, we do not need to migrate\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // Update version\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // We paginate through the users to avoid loading all users into memory at once\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // We don't need to update anything since the birthYear getter is used\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\nSi tienes que migrar muchos datos, considera utilizar un isolate en segundo plano para prevenir efectos no deseados en la UI.\n:::\n"
  },
  {
    "path": "docs/docs/es/recipes/full_text_search.md",
    "content": "---\ntitle: Full-text search\n---\n\n# Full-text search\n\nFull-text search es una manera poderosa de buscar texto en la base de datos. Ya deberías estar familiarizado con la forma en que funcionan los [índices](/es/indexes), pero vayamos sobre lo básico.\n\nUn índice funciona como una tabla de lookup, permitiéndole al motor de consulta encontrar rápidamente registros con un cierto valor. Por ejemplo, si tienes un campo `title` en tu objeto, puedes crear un índice en ese campo para hacer más rápida la búsqueda de objetos con un título determinado.\n\n## Porqué full-text search es útil?\n\nPuedes buscar texto fácilmente usando filtros. Existen algunas operaciones sobre string como por ejemplo `.startsWith()`, `.contains()` y `.matches()`. El problema con los filtros es que su tiempo de ejecución es de `O(n)` donde `n` es la cantidad de registros en la colección. Operaciones sobre strings como `.matches()` son especialmente costosas.\n\n:::tip\nFull-text search es mucho más rápido que los filtros, pero los índices tienen cietas limitaciones. En este receta vamos a explorar cómo sobrepasar esas limitaciones.\n:::\n\n## Exemplo básico\n\nLa idea es siempre la misma: En lugar de indexar el texto completo, indexamos las palabras en el texto así podemos buscar por ellos individualmente.\n\nCreamos el índice full-text más básico:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nAhora podemos buscar mensajes que contengan palabras específicas en el contenido:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nEsta consulta es súper rápida, pero existen algunos problemas:\n\n1. Sólo podemos buscar palabras completas\n2. No consideramos puntuación\n3. No soportamos otros caracteres de separación de palabras\n\n## Separando el texto de la forma correcta\n\nIntentemos mejorar el ejemplo anterior. Podemos intentar desarrollar una expresión regular complicada para correjir la separación de las palabras, pero sería lento e incorrecto para casos de borde.\n\nEl [Anexo Unicode #29](https://unicode.org/reports/tr29/) define cómo separar palabras correctamente para la mayoría de los idiomas. Es algo complicado, pero afortunadamente Isar hace el trabajo pesado por nosotros:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## Quiero más control\n\nPan comido! Podemos cambiar nuestro índice para soportar también coincidencia por prefijo y por mayúsculas y minúsculas:\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nDe manera predeterminada, Isar almacenará las palabras como valores hash que es rápido y eficiente en cuanto a espacio. Pero los hashes nos pueden usarse para coincidencia por prefijo. Usando `IndexType.value`, podemos cambiar el índice para usar las palabras directamente. Ésto nos ofrece la cláusula `.titleWordsAnyStartsWith()`:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## También necesito `.endsWith()`\n\nPor supuesto! Usaremos un truco para conseguir la coincidencia `.endsWith()`:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nNo olvides invertir la terminación que quieres buscar:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Algoritmos de derivación\n\nDesafortunadamente, los índices no soportan coincidencia por `.contains()` (ésto también es cierto para otras bases de datos). Pero existen algunas alternativas que vale la pena explorar. La elección depende fuertemente de su uso. Un ejemplo es indexar la raíz de la palabra en de la misma completa.\n\nUn algoritmo de derivación (stemming algorithm) es un proceso de normalización linguística en el cual las diferentes formas de una palabra se reducen a una forma común:\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\nAlgoritmos populares son el [Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) y el [Snowball stemming algorithms](https://snowballstem.org/algorithms/).\n\nExisten también formas avanzadas como [lematización](https://es.wikipedia.org/wiki/Lematizaci%C3%B3n).\n\n## Algoritmos de fonética\n\nUn [algoritmo de fonética](https://en.wikipedia.org/wiki/Phonetic_algorithm) es un algoritmo para indexar palabras de acuerdo a su pronunciación. Es decir, te permite encontrar palabras que \"suenan\" parecido a las que estás buscando.\n\n:::warning\nLa mayoría de los algoritmos de fonética sólo soportan un solo idioma.\n:::\n\n### Soundex\n\n[Soundex](https://es.wikipedia.org/wiki/Soundex) es un algoritmo de fonética para indexar nombres por sonido, como se pronuncian en inglés. El objetivo es que los homófonos se codifiquen con la misma representación para que produzcan coincidencias a pesar de alguna menor diferencia de tipeo. Es un algoritmo directo, y existen múltiples versiones mejoradas.\n\nUsando este algoritmo, ambos `\"Robert\"` y `\"Rupert\"` retornan la cadena `\"R163\"` mientras que `\"Rubin\"` entrega `\"R150\"`. `\"Ashcraft\"` y `\"Ashcroft\"` ambos entregan `\"A261\"`.\n\n### Metáfono doble\n\nEl algoritmo de codificado fonético [Metáfono doble](https://es.wikipedia.org/wiki/Metaphone) es la segunda generación de este tipo de algoritmos. Contiene varias mejoras fundamentales de diseño sobre el algoritmo de metáfono original.\n\nDoble Metáfono da cuenta de varias irregularidades en inglés de eslavo, alemán, celta, griego, francés, italiano, español, chino, y otros orígenes.\n"
  },
  {
    "path": "docs/docs/es/recipes/multi_isolate.md",
    "content": "---\ntitle: Uso de múltiples Isolates\n---\n\n# Uso de múltiples Isolates\n\nEn lugar de tareas, todo el código de Dart corre dentro de isolates. Cada isolate tiene su propia cabecera de memoria, asegurando que ningún estado de un isolate es accesible desde otro isolate.\n\nIsar puede accederse desde múltiples isolates al mismo tiempo, e incluso los observadores funcionan entre isolates. En esta receta, veremos cómo utilizar Isar en un entorno con múltiples isolates.\n\n## Cuándo usar múltiples isolates\n\nLas transacciones Isar se ejecutan en paralelo incluso dentro de un mismo isolate. En algunos casos, es también beneficioso acceder a Isar desde múltiples isolates.\n\nEl motivo es que Isar tarda algo de tiempo codificando y decodificando datos desde y hacia objetos Dart. Puedes pensarlo como codificando y decodificando JSON (sólo que más eficiente). Estas operaciones corren dentro del isolate en el cual se acceden los datos y naturalmente bloquean otro código en el isolate. En otras palabras: Isar ejecuta parte del trabajo dentro de tu isolate Dart.\n\nSi sólo necesitas leer y escribir algunos cientos de objetos de una vez, hacerlo dentro del mismo isolate que la UI no es un problema. Pero para transacciones muy grandes o si el isolate de la UI ya está bastante ocupado, deberías considerar usar un isolate separado.\n\n## Ejemplo\n\nLo primero que debemos hacer es abrir Isar en el nuevo isolate. Dado que la instancia de Isar ya está abierta en el isolate principal, `Isar.open()` retornará la misma instancia.\n\n:::warning\nAsegúrate de proveer los mismos esquemas que en el isolate principal. De lo contrario, obtendrás un error.\n:::\n\n`compute()` inicia un nuevo isolate en Flutter y ejecuta la función dada en él.\n\n```dart\nvoid main() {\n  // Open Isar in the UI isolate\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // listen to changes in the database\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // start a new isolate and create 10000 messages\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // after some time:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// function that will be executed in the new isolate\nFuture createDummyMessages(int count) async {\n  // we don't need the path here because the instance is already open\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // we use a synchronous transactions in isolates\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nExisten algunos aspectos interesantes a notar en este ejemplo:\n\n- `isar.messages.watchLazy()` se llama en el isolate de la UI y es notificado de los cambios desde otro isolate.\n- Las instancias son referenciadas por nombre. El nombre por defecto es `default`, pero en este ejemplo, utilizamos `myInstance`.\n- Utilizamos una transacción síncrona para crear los mensajes. Bloquear el nuevo isolate no es un problema, y las transacciones síncronas son algo más rápidas.\n"
  },
  {
    "path": "docs/docs/es/recipes/string_ids.md",
    "content": "---\ntitle: Ids de texto\n---\n\n# Ids de texto\n\nEsta es uno de los pedidos más frecuentes para Isar, por eso les dejamos este tutorial sobre como usar ids de texto.\n\nIsar no soporta ids de texto de forma nativa, y existe una buena razón para eso: los ids de enteros son mucho más eficientes y rápidos. Especialmente para enlaces, el gasto de un id de texto es demasiado significativo.\n\nEs probable que necesites almacenar datos externos que usen UUIDs o otro id no entero. Se recomienda almacenar el id de texto como una propiedad del objeto y usar una función rápida de hash para generar un entero de 64 bits que pueda ser usado como Id.\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nDe esta maneras obtienes lo mejor de dos mundos: Ids enteros eficientes para los enlaces y la posibilidad de usar ids de texto.\n\n## Función rápida de hash\n\nIdealmente, tu función de hash debería ser rápida y de alta calidad (sin colisiones). La siguiente es una implementación recomendada:\n\n```dart\n/// FNV-1a 64bit hash algorithm optimized for Dart Strings\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nSi eliges una función de hash diferente, asegúrate de que retorne un entero de 64-bit. Evita usar funciones hash criptográficas porque son mucho más lentas.\n\n:::warning\nEvita usar `string.hashCode` porque no está garantizada la estabilidad entre distintas plataformas y versiones de Dart.\n:::\n"
  },
  {
    "path": "docs/docs/es/schema.md",
    "content": "---\ntitle: Esquema\n---\n\n# Esquema\n\nCuando usas Isar para almacenar los datos de tu aplicación, estás tratando con colecciones. Una colección es como una tabla en la base de datos Isar asociada y sólo puede contener un tipo de objeto Dart. Cada objeto de la colección representa una línea de datos en la tabla correspondiente.\n\nLa definición de una colección es llamada \"esquema\" (\"schema\" en inglés). El generador Isar hará el trabajo pesado por ti y generará la mayoría del código que necesitas para usar tu colección.\n\n## Anatomía de una colección\n\nCada colección Isar se define anotando una clase con `@collection` o `@Collection()`. Una colección Isar incluye campos para cada columna en la tabla correspondiente en la base de datos, incluyendo uno que corresponde a la clave primaria.\n\nEl código siguiente es un ejemplo de una colección simple que define una table `User` con columnas para ID, nombre, y apellido:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nPara almacenar un campo, Isar debe tener acceso al mismo. Puedes asegurarte que Isar tiene acceso a un campo haciéndolo público o proporcionando métodos getter y setter.\n:::\n\nExisten algunos parámetros opcionales para personalizar la colección:\n\n| Configuración | Descripción                                                                                                                 |\n| ------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Controla si los campos de la clase padre y mixins serán almacenados en Isar. Habilitado por defecto.                        |\n| `accessor`    | Permite renombrar el punto de acceso por defecto de la colección (por ejemplo `isar.contacts` para la colección `Contact`). |\n| `ignore`      | Permite ignorar ciertas propiedades. Éstas también son respetadas para las super clases.                                    |\n\n### Isar Id\n\nCada clase que defina una colección Isar, debe definir una propiedad id y debe ser de tipo `Id` identificando inequívocamente un objecto. `Id` es simplemente un alias para `int` que le permite al generador Isar reconocer la propiedad id.\n\nIsar indexa automáticamente los campos id, que permite obtener y modificar objectos de manera eficiente basándose en su id.\n\nPuedes establecer tus propios ids o pedir a Isar que asigne un id auto-incrementable. Si el campo `id` es `null` y no `final`, Isar asignará un id auto-incrementable. Si quieres un id auto-incrementable y no-null, puedes usar `Isar.autoIncrement` en lugar de `null`.\n\n:::tip\nLos ids auto incrementables no se reusan cuando un objeto es eliminado. La única manera de reiniciar los ids auto incrementables es borrando la base de datos.\n:::\n\n### Renombrando colecciones y campos\n\nPor defecto, Isar usa el nombre de la clase como nombre de la colección. De manera similar, Isar usa los nombres de los campos como nombres de las columnas en la base de datos. Si quieres que una colección o campo tenga un nombre diferente, agrega la anotación `@Name`. El ejemplo siguiente demuestra el uso de nombres personalizados para colecciones y campos:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nEspecíficamente si quieres renombrar objectos Dart o clases que ya están almacenados en la base de datos, deberías considerar usar la anotación `@Name`. De otra manera, la base de datos eliminiará y creará nuevamente el campo o la colección.\n\n### Ignorando campos\n\nIsar almacena todos los campos públicos de una clase que defina una colección. Anotando una propiedad o getter con `@ignore`, puedes excluir dicha propiedad del almacenamiento, como se muestra en el siguiente extracto de código:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nEn los casos donde una colección hereda los campos de una colección padre, es generalmente más fácil usar la propiedad `ignore` de la anotación `@Collection`:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nSi una colección contiene un campo con un tipo de dato no soportado por Isar, éste campo debe ser ignorado.\n\n:::warning\nTen en cuenta que no es una buena práctica guardar información en objectos Isar que no serán almacenados.\n:::\n\n## Tipos de datos soportados\n\nIsar soporta los siguientes tipos de datos:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nAdicionalmente, Isar soporta objetos embebidos y enums. Explicaremos éstos más adelante.\n\n## byte, short, float\n\nPara muchos casos de uso, no es necesario el rango completo de 64-bits de un entero o punto flotante (int o double). Isar contiene soporte para tipos adicionales que te permiten ahorrar espacio y memoria cuando se almacenan número más pequeños.\n\n| Tipo       | Tamaño en bytes | Rango                                                  |\n| ---------- | --------------- | ------------------------------------------------------ |\n| **byte**   | 1               | 0 a 255                                                |\n| **short**  | 4               | -2,147,483,647 a 2,147,483,647                         |\n| **int**    | 8               | -9,223,372,036,854,775,807 a 9,223,372,036,854,775,807 |\n| **float**  | 4               | -3.4e38 a 3.4e38                                       |\n| **double** | 8               | -1.7e308 a 1.7e308                                     |\n\nLos tipos numéricos adicionales con sólo aliases de los tipos de datos nativos de Dart, por lo que usar `short`, por ejemplo, funciona de la misma manera que usando `int`.\n\nEl siguiente es un ejemplo de una colección que contiene todos los tipos de datos vistos anteriormente:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nTodos los tipos numéricos también pueden ser usados en listas. Para almacenar bytes, deberías usar `List<byte>`.\n\n## Tipos nulables\n\nEntender cómo funciona la nulabilidad en Isar en esencial: los tipos numéricos **NO** tienen una representación `null` dedicada. Por el contrario, un valor específico es usado:\n\n| Tipo       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`, `String`, y `List` tienen una representación `null` por separado.\n\nEste comportamiento habilita mejoras en el rendimiento, y te permite cambiar libremente la nulabilidad de tus campos sin requerir una migración o código especial para lidiar con valores `null`.\n\n:::warning\nEl tipo `byte` no soporta valores null.\n:::\n\n## DateTime\n\nIsar no almacena información de zonas horarias en tus campos DateTime. En su lugar, convierte `DateTime`s a UTC antes de almacenarlos. Isar devuelve todas las fechas en hora local.\n\nLos `DateTime`s se almacenan con presición de microsegundos. En navegadores web, sólo se soporta presición de milisegundos debido a una limitación de Javascript.\n\n## Enum\n\nIsar permite almacenar y usar enums como cualquier otro tipo de dato Isar. Sin embargo, tendrás que decidir cómo Isar debería representar el enum en el disco. Isar soporta cuatro estrategias diferentes:\n\n| EnumType    | Descripción                                                                                      |\n| ----------- | ------------------------------------------------------------------------------------------------ |\n| `ordinal`   | El índice del emun se almacena como `byte`. Esto es muy eficiente pero no permite enums nulables |\n| `ordinal32` | El índice del enum se almacena como `short` (entero de 4 bytes).                                 |\n| `name`      | El nombre del enum se almacena como `String`.                                                    |\n| `value`     | Para recuperar el valor del enum se utiliza una propiedad personalizada.                         |\n\n:::warning\n`ordinal` y `ordinal32` dependen del orden de los valores en el enum. Si cambias el orden, bases de datos existentes retornarán valores incorrectos.\n:::\n\nVeamos un ejemplo para cada estrategia.\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // same as EnumType.ordinal\n  late TestEnum byteIndex; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nPor supuesto, los Enums pueden usarse también en listas.\n\n## Objetos Embebidos\n\nCon frecuencia es útil tener objetos anidados en tus colecciones. No hay límite en cuanto a la profundidad que un objeto anidado puede tener. Sin embargo, es necesario tener en cuenta que actualizar un objeto anidado requerirá escribir el árbol completo del objeto en la base de datos.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nLos objectos embebidos pueden ser nulables y extender otros objectos. El único requerimiento es que sean anotados con `@embedded` y que tengan un constructor predeterminado sin parámetros requeridos.\n"
  },
  {
    "path": "docs/docs/es/transactions.md",
    "content": "---\ntitle: Transacciones\n---\n\n# Transacciones\n\nEn Isar, las transacciones combinan múltiples operaciones en una sola unidad de trabajo. La mayoría de las interacciones con Isar utilizan transacciones de forma implícita. El acceso de lectura y escritura en Isar cumple está conforme con [ACID](https://es.wikipedia.org/wiki/ACID). Las transacciones se retroceden automáticamente en caso de error.\n\n## Transacciones explícitas\n\nEn una transacción explícita, obtienes una instantánea consistente de la base de datos. Intenta minimizar la duración de las transacciones. En una transacción stá prohibido hacer llamadas de red u otras operaciones de largo procesamiento.\n\nLas transacciones (especialmente las de escritura) tienen un costo, y siempre deberías agrupar operaciones sucesivas en una sola transacción.\n\nLas transacciones puede ser tanto síncronas como asíncronas. En las transacciones síncronas, sólo puedes utilizar operaciones síncronas. En las transacciones asíncronas, sólo operaciones asíncronas.\n\n|            | Lectura      | Lectura y Escritura |\n| ---------- | ------------ | ------------------- |\n| Síncronas  | `.txnSync()` | `.writeTxnSync()`   |\n| Asíncronas | `.txn()`     | `.writeTxn()`       |\n\n### Transacciones de lectura\n\nLas transacciones explícitas de lectura son opcionales, pero te permiten hacer lecturas atómicas y confiar en que el estado de la base de datos dentro de la transacción será consistente. Internamente Isar utiliza transacciones de lectura implícitas para todas las operaciones de lectura.\n\n:::tip\nLas transacciones de lectura asíncronas se ejecutan en paralelo con otras transacciones de lectura y escritura. Genial verdad?\n:::\n\n### Transacciones de escritura\n\nA diferencia de las operaciones de lectura, las operaciones de escritura en Isar deben ser agrupadas en una transacción explícita.\n\nCuando una transacción de escritura finaliza exitosamente, automáticamente es aplicada, y todos los cambios se escriben al disco. En case de error, se aborta y los cambios retroceden. Las transacciones son \"todo o nada\": o todas las escrituras de la transacción suceden, o ninguna de ellas tiene efecto, para garantizar la consitencia de los datos.\n\n:::warning\nCuando una operación de la base de datos falla, la transacción se aborta y ya no debe ser utilizada. Incluso si capturaste el error en Dart.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: move loop inside transaction\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/es/tutorials/quickstart.md",
    "content": "---\ntitle: Inicio rápido\n---\n\n# Inicio rápido\n\nIncreíble!, estás aquí! Vamos a empezar a usar la base de datos más genial que existe para Flutter...\n\nVamos a ser cortos en palabras para ir inmediatamente al código en esta guía de inicio rápido.\n\n## 1. Agrega las dependencias\n\nAntes de empezar la parte divertida, necesitamos agregar algunos paquetes al `pubspec.yaml`. Podemos usar pub para hacer el trabajo pesado por nosotros.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. Anota las clases\n\nAnota tus clases de colecciones con `@collection` y elige un campo `Id`.\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // you can also use id = null to auto increment\n\n  String? name;\n\n  int? age;\n}\n```\n\nLos Ids identifican inequívocamente los objetos en una colección y te permiten luego buscarlos nuevamente.\n\n## 3. Ejecuta el generador de código\n\nEjecuta el siguiente comando para iniciar el `build_runner`:\n\n```\ndart run build_runner build\n```\n\nSi estás usando Flutter, puedes usar el siguiente:\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Abre una instancia Isar\n\nAbre una nueva instalcia Isar y pásale todos los esquemas de tu colección. Opcionalmente puedes especificar un nombre para la instancia y un directorio.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. Lee y escribe\n\nUna vez que tu base de datos está abierta, puedes comenzar a usar tus colecciones.\n\nTodas las operaciones CRUD básicas están disponibles a través del `IsarCollection`.\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // insert & update\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // get\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // delete\n});\n```\n\n## Otros recursos\n\nGustas de aprender de manera visual? Dale un vistazo a estos videos para empezar con Isar (Advertencia, material en Inglés):\n\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/es/watchers.md",
    "content": "---\ntitle: Watchers\n---\n\n# Watchers\n\nIsar te permite suscribirte a los cambios en la base de datos. Puedes \"observar\" los cambios en un objeto específico, una colección entera, o una consulta.\n\nLos watchers te permiten reaccionar a los cambios en la base de datos de manera eficiente. Puedes por ejemplo refrescar la interfaz de usuario cuando se agrega un contacto, enviar una consulta de red cuando un documento se actualiza, etc.\n\nUn watcher es notificado después que una transacción finaliza exitosamente y el objeto realmente cambia.\n\n## Observando objetos\n\nSi quieres ser notificado cuando un objeto específico se crea, actualiza o elimina, debes \"observar\" un objeto:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\nComo puedes ver en el ejemplo anterior, el objeto no necesita existir aún. El watcher será notificado cuando se crea.\n\nExiste un parámetro adicional `fireImmediately`. Si lo seteas en `true`, Isar agregará inmediatamente el valor actual del objeto al stream.\n\n### Lazy watching\n\nTal vez no necesitas recibir el nuevo valor pero sólo ser notificado sobre el cambio. Esto evita que Isar tenga get obtener el objeto:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## Observando collections\n\nEn lugar de observar un solo objeto, puedes hacerlo con una colección completa y ser notificado cuando cualquier objeto se agrega, actualiza o elimina:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## Observando consultas\n\nIncluso es posible observar consultas. Isar lo hace mejor incluso al notificarte sólo si el resultado de la consulta en realidad cambia. No serás notificado de los cambios provocados por un enlace. Observa una colección si quieres ser notificado acerca de los cambios en enlaces.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nSi en tus consultas usas offset y límite o distinct, Isar incluso te notificará cuando los objetos coinciden con el filtro pero caen fuera de la consulta.\n:::\n\nAl igual que `watchObject()`, puedes usar `watchLazy()` para ser notificado cuando el resultado de la consulta cambia pero sin obtenerlos.\n\n:::danger\nEjecutar consultas repetidamente para cada cambio es muy ineficiente. Sería mejor si usaras un watcher perezoso sobre la colección.\n:::\n"
  },
  {
    "path": "docs/docs/faq.md",
    "content": "---\ntitle: FAQ\n---\n\n# Frequently Asked Questions\n\nA random collection of frequently asked questions about Isar and Flutter databases.\n\n### Why do I need a database?\n\n> I store my data in a backend database, why do I need Isar?.\n\nEven today, it is very common to have no data connection if you are in a subway or a plane or if you visit your grandma, who has no Wi-Fi and a very bad cell signal. You shouldn't let bad connection cripple your app!\n\n### Isar vs Hive\n\nThe answer is easy: Isar was [started as a replacement for Hive](https://github.com/hivedb/hive/issues/246) and is now at a state where I recommend always using Isar over Hive.\n\n### Where clauses?!\n\n> Why do **_I_** have to choose which index to use?\n\nThere are multiple reasons. Many databases use heuristics to choose the best index for a given query. The database needs to collect additional usage data (-> overhead) and might still choose the wrong index. It also makes creating a query slower.\n\nNobody knows your data better than you, the developer. So you can choose the optimal index and decide for example whether you want to use an index for querying or sorting.\n\n### Do I have to use indexes / where clauses?\n\nNope! Isar is most likely fast enough if you only rely on filters.\n\n### Is Isar fast enough?\n\nIsar is among the fastest databases for mobile, so it should be fast enough for most use cases. If you run into performance issues, chances are that you are doing something wrong.\n\n### Does Isar increase the size of my app?\n\nA little bit, yes. Isar will increase the download size of your app by about 1 - 1.5 MB. Isar Web adds only a few KB.\n\n### The docs are incorrect / there is a typo.\n\nOh no, sorry. Please [open an issue](https://github.com/isar-community/isar/issues/new/choose) or, even better, a PR to fix it 💪.\n"
  },
  {
    "path": "docs/docs/fr/README.md",
    "content": "---\nhome: true\ntitle: Acceuil\nheroImage: /isar.svg\nactions:\n  - text: Commençons !\n    link: /fr/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Fait pour Flutter\n    details: Configuration minimale, facilité d'utilisation. Il suffit d'ajouter quelques lignes de code pour commencer.\n  - title: 🚀 Hautement extensible\n    details: Stockez des centaines de milliers d'entrées dans une seule base de données NoSQL et filtrer-les de manière efficace et asynchrone.\n  - title: 🍭 Riche en fonctionnalités\n    details: Isar dispose d'un riche ensemble de fonctionnalités pour vous aider à gérer vos données. Index composés et multi-entrées, modificateurs de requête, support JSON, etc.\n  - title: 🔎 Recherche plein texte\n    details: Isar dispose d'une recherche plein texte intégrée. Créez un index à entrées multiples et recherchez facilement des entrées.\n  - title: 🧪 Sémantique ACID\n    details: Isar est conforme à la norme ACID et gère les transactions automatiquement. Il annule les modifications si une erreur se produit.\n  - title: 💃 Types statiques\n    details: Les requêtes d'Isar sont typées statiquement et vérifiées à la compilation. Pas besoin de se soucier des erreurs d'exécution.\n  - title: 📱 Multiplatforme\n    details: Support pour iOS, Android, Desktop et WEB!\n  - title: ⏱ Asynchrone\n    details: Opérations de requête parallèles et support multi-Isolate prêts à l'emploi.\n  - title: 🦄 Open Source\n    details: Tout est open source et gratuit pour toujours!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/fr/crud.md",
    "content": "---\ntitle: Création, lecture, modification, suppression\n---\n\n# Création, lecture, modification, suppression\n\nMaintenant que nous avons défini nos collections, apprenons à les manipuler!\n\n## Ouverture de Isar\n\nAvant de pouvoir faire quoi que ce soit, nous avons besoin d'une instance Isar. Chaque instance nécessite un répertoire avec droits d'écriture où le fichier de la base de données peut être stocké. Si vous ne spécifiez pas de répertoire, Isar trouvera un répertoire par défaut selon la plateforme actuelle.\n\nFournissez tous les schémas que vous souhaitez utiliser avec l'instance Isar. Si nous ouvrons plusieurs instances, nous devons toujours fournir les mêmes schémas à chaque instance.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [ContactSchema],\n  directory: dir.path,\n);\n```\n\nNous pouvons utiliser la configuration par défaut ou fournir certains des paramètres suivants:\n\n| Config              | Description                                                                                                                                                                                                                              |\n|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `name`              | Ouvrez plusieurs instances avec des noms distincts. Par défaut, `\"default\"` est utilisé.                                                                                                                                                 |\n| `directory`         | L'emplacement de stockage de cette instance. Nous pouvons passer un chemin relatif ou absolu. Par défaut, `NSDocumentDirectory` est utilisé pour iOS et `getDataDirectory` pour Android. Non requis pour Web.                            |\n| `relaxedDurability` | Assouplit la garantie de durabilité pour augmenter les performances d'écriture. En cas de crash du système (pas de crash de l'application), il est possible de perdre la dernière transaction validée. La corruption n'est pas possible. |\n| `compactOnLaunch`   | Conditions pour vérifier si la base de données doit être compactée lors de l'ouverture de l'instance.                                                                                                                                    |\n| `inspector`         | Active l'inspecteur en mode debug. Cette option est ignorée en mode profile et release.                                                                                                                                                  |\n\nSi une instance est déjà ouverte, l'appel à `Isar.open()` donnera l'instance existante sans tenir compte des paramètres spécifiés. Utile pour utiliser Isar dans un isolat.\n\n:::tip\nEnvisagez d'utiliser le package [path_provider](https://pub.dev/packages/path_provider) pour obtenir un chemin valide sur toutes les plateformes.\n:::\n\nL'emplacement de stockage du fichier de la base de données est `répertoire/nom.isar`.\n\n## Lecture de la base de données\n\nUtilisez les instances de `IsarCollection` pour trouver, filtrer et créer de nouveaux objets d'un type donné dans Isar.\n\nPour les exemples ci-dessous, nous supposons que nous avons une collection `Recipe` définie comme suit:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### Obtenir une collection\n\nToutes nos collections vivent dans l'instance Isar. Nous pouvons obtenir la collection avec:\n\n```dart\nfinal recipes = isar.recipes;\n```\nN'oubliez pas d'importer les méthodes d'extension afin d'accéder à la collection depuis l'instance isar.\n\nC'était facile! Si vous ne voulez pas utiliser les accesseurs de collection, vous pouvez aussi utiliser la méthode `collection()`:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### Obtenir un objet (par id)\n\nNous n'avons pas encore de données dans la collection, mais faisons comme si c'était le cas afin de récupérer un objet imaginaire avec l'identifiant `123`.\n\n```dart\nfinal recipe = await recipes.get(123);\n```\n\n`get()` renvoie une `Future` avec soit l'objet, soit `null` s'il n'existe pas. Toutes les opérations d'Isar sont asynchrones par défaut, et la plupart d'entre elles ont un équivalent synchrone:\n\n```dart\nfinal recipe = recipes.getSync(123);\n```\n\n:::warning\nVous devriez utiliser la version asynchrone des méthodes par défaut dans votre isolat d'interface utilisateur. Comme Isar est très rapide, il est souvent acceptable d'utiliser la version synchrone.\n:::\n\nSi nous voulons récupérer plusieurs objets à la fois, nous pouvons utiliser `getAll()` ou `getAllSync()`:\n\n```dart\nfinal recipe = await recipes.getAll([1, 2]);\n```\n\n### Recherche d'objets\n\nAu lieu de récupérer les objets par leur identifiant, nous pouvons également obtenir une liste d'objets répondant à certaines conditions en utilisant `.where()` et `.filter()`:\n\n```dart\nfinal allRecipes = await recipes.where().findAll();\n\nfinal favouires = await recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ En savoir plus: [Requêtes](queries)\n\n## Modifier la base de données\n\nIl est enfin temps de modifier notre collection! Pour créer, mettre à jour ou supprimer des objets, utilisez les opérations respectives dans une transaction d'écriture:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await recipes.get(123)\n\n  recipe.isFavorite = false;\n  await recipes.put(recipe); // Effectuer des opérations de mise à jour\n\n  await recipes.delete(123); // Ou des opérations de suppression\n});\n```\n\n➡️ En savoir plus: [Transactions](transactions)\n\n### Insertion d'objets\n\nPour faire persister un objet dans Isar, insérons-le dans une collection. La méthode `put()` d'Isar va soit insérer, soit mettre à jour l'objet selon s'il existe déjà dans la collection ou non.\n\nSi le champ id est `null` ou `Isar.autoIncrement`, Isar utilisera un id auto-incrémenté.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await recipes.put(pancakes);\n})\n```\n\nIsar attribuera automatiquement l'id à l'objet si le champ `id` est non final.\n\nIl est tout aussi facile d'insérer plusieurs objets à la fois:\n\n```dart\nawait isar.writeTxn(() async {\n  await recipes.putAll([pancakes, pizza]);\n})\n```\n\n### Mise à jour d'objets\n\nLa création et la mise à jour fonctionnent toutes deux avec `collection.put(object)`. Si l'id est `null` (ou n'existe pas), l'objet est créé; sinon, il est mis à jour.\n\nDonc si nous voulons défavoriser nos crêpes, nous pouvons faire ce qui suit:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await recipes.put(pancakes);\n});\n```\n\n### Suppression d'objets\n\nVous voulez vous débarrasser d'un objet dans Isar ? Utilisez `collection.delete(id)`. La méthode `delete` retourne si un objet avec l'identifiant spécifié a été trouvé et supprimé. Si nous désirons supprimer l'objet avec l'identifiant `123`, par exemple, nous pouvons faire:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nComme pour les opérations `get` et `put`, il existe également une opération de suppression en vrac qui renvoie le nombre d'objets supprimés:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\nSi nous ne connaissons pas les identifiants des objets que nous voulons supprimer, nous pouvons utiliser une requête:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/fr/faq.md",
    "content": "---\ntitle: FAQ\n---\n\n# Foire aux questions\n\nUne compilation de questions fréquemment posées sur les bases de données Isar et Flutter.\n\n### Pourquoi ai-je besoin d'une base de données?\n\n> Je stocke mes données dans une base de données backend, pourquoi ai-je besoin d'Isar?\n\nAujourd'hui encore, il est très courant de ne pas avoir de connexion internet si vous êtes dans le métro, dans l'avion, ou si vous rendez visite à votre grand-mère, qui n'a pas de WiFi et un très mauvais signal cellulaire. Vous ne devez pas laisser une mauvaise connexion paralyser votre application!\n\n### Isar vs Hive\n\nLa réponse est simple: Isar a été [lancé comme un remplacement de Hive](https://github.com/hivedb/hive/issues/246) et est maintenant à un stade où on recommande de toujours utiliser Isar plutôt que Hive.\n\n### Clauses `where`?!\n\n> Pourquoi est-ce que **_je_** dois choisir quel index utiliser?\n\nIl y a plusieurs raisons. De nombreuses bases de données utilisent des heuristiques pour choisir le meilleur index pour une requête donnée. La base de données doit collecter des données d'utilisation supplémentaires (-> temps de traitement plus grand) et peut toujours choisir le mauvais index. Cela rend également la création d'une requête plus lente.\n\nPersonne ne connaît mieux vos données que vous, le développeur. Vous pouvez donc choisir l'index optimal et décider, par exemple, si vous voulez utiliser un index pour la requête ou le tri.\n\n### Dois-je utiliser des index / clauses `where`?\n\nNon! Isar est très probablement assez rapide si vous ne comptez que sur les filtres.\n\n### Isar est-il suffisamment rapide ?\n\nIsar est l'une des bases de données les plus rapides pour les appareils mobiles, et devrait donc être suffisamment rapide pour la plupart des cas d'utilisation. Si vous rencontrez des problèmes de performances, il y a de fortes chances que vous fassiez quelque chose de mal.\n\n### Isar augmente-t-il la taille de mon application?\n\nUn peu, oui. Isar augmentera la taille de téléchargement de votre application d'environ 1 à 1,5 Mo. Isar Web n'ajoute que quelques Ko.\n\n### La documentation est incorrecte / il y a une erreur de frappe.\n\nOh non, désolé. Veuillez [ouvrir un ticket](https://github.com/isar-community/isar/issues/new/choose) ou, mieux encore, un PR pour le résoudre 💪.\n"
  },
  {
    "path": "docs/docs/fr/indexes.md",
    "content": "---\ntitle: Indices\n---\n\n# Indices\n\nLes indices (`index`) sont la fonctionnalité la plus puissante d'Isar. De nombreuses bases de données embarquées proposent des index \"normaux\" (voire aucun), mais Isar dispose également d'index composés et à entrées multiples. Il est essentiel de comprendre le fonctionnement des index pour optimiser les performances des requêtes. Isar vous permet de choisir l'index que vous voulez utiliser et comment vous voulez l'utiliser. Nous allons commencer par une introduction rapide à ce que sont les index.\n\n## Que sont les indices?\n\nLorsqu'une collection n'est pas indexée, l'ordre des lignes ne pourra probablement pas être discerné par la requête comme étant optimisé de quelconques manières, et votre requête devra donc rechercher les objets de façon linéaire. En d'autres termes, la requête devra parcourir chaque objet pour trouver ceux qui correspondent aux conditions. Comme vous pouvez l'imaginer, cela peut prendre du temps. La recherche dans chaque objet n'est pas très efficace.\n\nPar exemple, cette collection `Product` est entièrement non ordonnée.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**Données:**\n\n| id  | name      | price |\n|-----|-----------|-------|\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nUne requête qui tente de trouver tous les produits dont le prix est supérieur à 30 € doit parcourir les neuf rangées. Ce n'est pas un problème pour neuf lignes, mais cela peut le devenir pour 100 000 lignes.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nPour améliorer les performances de cette requête, nous indexons la propriété `price`. Un index est comme une table de recherche triée:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**Index généré:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nMaintenant, la requête peut être exécutée beaucoup plus rapidement. L'exécuteur peut directement sauter aux trois dernières lignes d'index et trouver les objets correspondants par leur id.\n\n### Triage\n\nAutre point intéressant: les index peuvent effectuer des tris très rapides. Les requêtes triées sont coûteuses, car la base de données doit charger tous les résultats en mémoire avant de les trier. Même si vous spécifiez un décalage ou une limite, ils sont appliqués après le tri.\n\nImaginons que nous voulions trouver les quatre produits les moins chers. Nous pourrions utiliser la requête suivante:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nDans cet exemple, la base de données devrait charger tous (!) les objets, les trier par prix et renvoyer les quatre produits dont le prix est le plus bas.\n\nComme vous pouvez probablement l'imaginer, cette opération peut être réalisée de manière beaucoup plus efficace avec l'index précédent. La base de données prend les quatre premières lignes de l'index et renvoie les objets correspondants puisqu'ils sont déjà dans le bon ordre.\n\nPour utiliser l'index pour le tri, nous devons écrire la requête comme suit:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nLa clause `where` `.anyX()` indique à Isar d'utiliser un index uniquement pour le tri. Nous pouvons également utiliser une clause `where` comme `.priceGreaterThan()` et obtenir des résultats triés.\n\n## Indices uniques\n\nUn index unique garantit que l'index ne contient pas de valeurs en double. Il peut être composé d'une ou plusieurs propriétés. Si un index unique a une propriété, les valeurs de cette propriété seront uniques. Si l'index unique a plus d'une propriété, la combinaison des valeurs dans ces propriétés est unique.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nToute tentative d'insertion ou de mise à jour de données dans l'index unique qui provoque un doublon entraînera une erreur:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// Essayons d'insérer un utilisateur avec le même nom d'utilisateur\nawait isar.users.put(user2); // -> error: unique constraint violated\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## Remplacement d'indices\n\nIl n'est parfois pas préférable d'envoyer une erreur si une contrainte unique n'est pas respectée. Au lieu de cela, nous pouvons vouloir remplacer l'objet existant par le nouvel objet. Pour cela, il suffit de mettre la propriété `replace` de l'index à `true`.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nMaintenant, lorsque nous essayons d'insérer un utilisateur avec un nom déjà existant, Isar va remplacer l'utilisateur existant par le nouveau.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nLes indices de remplacement génèrent également des méthodes `putBy()` qui nous permettent de mettre à jour les objets au lieu de les remplacer. L'identifiant existant est réutilisé, et les liens sont toujours présents.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// L'utilisateur n'existe pas, donc c'est la même chose que put()\nawait isar.users.putByUsername(user1); \nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nComme nous pouvons le constater, l'identifiant du premier utilisateur inséré est réutilisé.\n\n## Index insensibles à la casse\n\nTous les index sur les propriétés `String` et `List<String>` sont sensibles à la casse par défaut. Si nous voulons créer un index insensible à la casse, nous pouvons utiliser l'option `caseSensitive`:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## Type d'indice\n\nIl existe différents types d'index. La plupart du temps, nous voudrons utiliser un index `IndexType.value`, mais les index de hachage sont plus efficaces.\n\n### Index `value`\n\nLes index de valeurs sont le type par défaut et le seul autorisé pour toutes les propriétés qui ne contiennent pas de chaînes de caractères ou de listes. Les valeurs des propriétés sont utilisées pour construire l'index. Dans le cas des listes, ce sont les éléments de la liste qui sont utilisés. Il s'agit du type d'index le plus flexible mais aussi le plus gourmand en espace parmi les trois types d'index.\n\n:::tip\nUtilisez `IndexType.value` pour les types primitifs, les chaînes de caractères lorsque vous avez besoin de clauses `startsWith()` et les listes si vous voulez rechercher des éléments individuels.\n:::\n\n### Index `hash`\n\nLes chaînes de caractères et les listes peuvent être hachées pour réduire de manière significative le stockage requis par l'index. L'inconvénient des index de hachage est qu'ils ne peuvent pas être utilisés pour les scans de préfixe (clauses `where` `startsWith`).\n\n:::tip\nUtilisez `IndexType.hash` pour les chaînes de caractères et les listes si vous n'avez pas besoin des clauses `where` `startsWith` et `elementEqualTo`.\n:::\n\n### Index `hashElements`\n\nLes listes de chaînes peuvent être hachées dans leur ensemble (à l'aide de `IndexType.hash`), ou les éléments de la liste peuvent être hachés séparément (à l'aide de `IndexType.hashElements`), créant ainsi un index à entrées multiples avec des éléments hachés.\n\n:::tip\nUtilisez `IndexType.hashElements` pour les `List<String>` où vous avez besoin de clauses `where` `elementEqualTo`.\n:::\n\n## Indices composés\n\nUn index composite est un index sur plusieurs propriétés. Isar nous permet de créer des index composites sur un maximum de trois propriétés.\n\nLes index composés sont également connus sous le nom d'index à colonnes multiples.\n\nIl est probablement préférable de commencer par un exemple. Nous créons une collection de personnes et définissons un index composé sur les propriétés âge et nom:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**Données:**\n\n| id  | name   | age | hometown  |\n|-----|--------|-----|-----------|\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**Index généré:**\n\n| age | name   | id  |\n|-----|--------|-----|\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nL'indice composé généré contient toutes les personnes triées par leur âge et leur nom.\n\nLes index composés sont parfaits si nous souhaitons créer des requêtes efficaces triées par plusieurs propriétés. Ils permettent également d'utiliser des clauses `where` avancées avec plusieurs propriétés :\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nLa dernière propriété d'un index composé supporte également des conditions telles que `startsWith()` ou `lessThan()` :\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Indices à entrées multiples\n\nSi nous indexons une liste en utilisant `IndexType.value`, Isar va automatiquement créer un index à entrées multiples, et chaque élément de la liste est indexé vers l'objet. Cela fonctionne pour tous les types de listes.\n\nLes applications pratiques des index à entrées multiples comprennent l'indexation d'une liste de balises ou la création d'un index en texte intégral.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` divise une chaîne de caractères en mots selon la spécification [Unicode Annex #29](https://unicode.org/reports/tr29/), ce qui fait qu'il fonctionne correctement pour presque toutes les langues.\n\n**Data:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nLes entrées comportant des mots en double n'apparaissent qu'une seule fois dans l'index.\n\n**Index généré:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nCet index peut maintenant être utilisé pour les clauses de préfixe (ou d'égalité) des mots individuels de la description.\n\n:::tip\nAu lieu de stocker les mots directement, vous pouvez également envisager d'utiliser le résultat d'un [algorithme phonétique](https://fr.wikipedia.org/wiki/Algorithme_phon%C3%A9tique) comme [Soundex](https://fr.wikipedia.org/wiki/Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/fr/limitations.md",
    "content": "# Limitations\n\nComme vous le savez, Isar fonctionne sur les appareils mobiles et les ordinateurs de bureau fonctionnant sur la VM ainsi que sur le Web. Ces deux plateformes sont très différentes, et ont donc des limitations différentes.\n\n## Limitations de la VM\n\n- Seuls les 1024 premiers octets d'une chaîne peuvent être utilisés pour un préfixe de clause `where`.\n- Les objets ne peuvent avoir une taille supérieure à 16 Mo\n\n## Limitations Web\n\nComme Isar Web est basé sur `IndexedDB`, il y a plus de limitations, mais elles sont à peine perceptibles lors de l'utilisation d'Isar.\n\n- Les méthodes synchrones ne sont pas supportées\n- Les filtres `Isar.splitWords()` et `.matches()` ne sont pas encore implémentés\n- Les changements de schémas ne sont pas autant vérifiés que dans la VM, il faut donc faire attention à respecter les règles\n- Tous les types de nombres sont stockés en tant que double (le seul type de nombre js), donc `@Size32` n'a aucun effet\n- Les index sont représentés différemment, donc les index de hachage n'utilisent pas moins d'espace (ils fonctionnent toujours de la même manière)\n- `col.delete()` et `col.deleteAll()` fonctionnent correctement, mais la valeur de retour n'est pas correcte\n- `col.clear()` ne réinitialise pas la valeur d'auto-incrémentation\n- `NaN` n'est pas supporté comme valeur\n"
  },
  {
    "path": "docs/docs/fr/links.md",
    "content": "---\ntitle: Liens\n---\n\n# Liens\n\nLes liens nous permettent d'exprimer des relations entre objets, comme l'auteur d'un commentaire (`User`). Nous pouvons modéliser des relations `1:1`, `1:n`, et `n:n` avec les liens Isar. L'utilisation de liens est moins ergonomique que l'utilisation d'objets embarqués. Il est donc préférable d'utiliser des objets embarqués lorsque possible.\n\nConsidérez le lien comme une table séparée qui contient la relation. Elle est similaire aux relations SQL, mais possède un ensemble de fonctionnalités et une API différente.\n\n## IsarLink\n\n`IsarLink<T>` peut contenir un ou plusieurs objets liés et peut être utilisé pour exprimer une relation de type \"un-à-un\". `IsarLink` a une seule propriété appelée `value` qui contient l'objet lié.\n\nLes liens ne sont pas chargés par default. Vous devez donc dire à `IsarLink` de charger ou de sauvegarder la `value` explicitement. Vous pouvez le faire en appelant `linkProperty.load()` et `linkProperty.save()`.\n\n:::tip\nLa propriété `id` des collections source et cible d'un lien doit être non finale.\n:::\n\nPour les plateformes autres que web, les liens sont chargés automatiquement lorsque vous les utilisez pour la première fois. Commençons par ajouter un `IsarLink` à une collection:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nNous avons défini un lien entre les enseignants et les élèves. Dans cet exemple, chaque élève peut avoir exactement un professeur.\n\nD'abord, nous créons le professeur et l'assignons à un étudiant. Nous devons `.put()` le professeur et sauvegarder le lien manuellement.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\nNous pouvons maintenant utiliser le lien :\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nEssayons la même chose avec du code synchrone. Nous n'avons pas besoin de sauvegarder le lien manuellement, car `.putSync()` sauvegarde automatiquement tous les liens. Il crée même le professeur pour nous.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nIl serait plus logique que l'étudiant de l'exemple précédent puisse avoir plusieurs professeurs. Heureusement, Isar a `IsarLinks<T>`, qui permet de contenir plusieurs objets liés et d'exprimer une relation de type \"à plusieurs\".\n\n`IsarLinks<T>` implémente `Set<T>` et expose toutes les méthodes qui sont autorisées pour les ensembles.\n\n`IsarLinks` se comporte comme `IsarLink` et n'est également pas changé par défaut. Pour charger tous les objets liés, nous devons utiliser `linkProperty.load()`. Pour persister les changements, `linkProperty.save()`.\n\nLa représentation interne de `IsarLink` et `IsarLinks` est la même. Nous pouvons faire évoluer le `IsarLink<Teacher>` d'avant en un `IsarLinks<Teacher>` pour assigner plusieurs professeurs à un seul étudiant (sans perdre de données).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nCela fonctionne étant donné que nous n'avons pas changé le nom du lien (`teacher`), donc Isar s'en souvient d'avant.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Backlinks\n\nJe vous entends demander: \"Et si nous voulions exprimer des relations inverses?\". Ne vous inquiétez pas, nous allons maintenant introduire les `Backlinks`.\n\nLes backlinks sont des liens en sens inverse. Chaque lien a toujours un backlink implicite. Nous pouvons le rendre disponible à notre application en annotant un `IsarLink` ou un `IsarLinks` avec `@Backlink()`.\n\nLes backlinks ne nécessitent pas de mémoire ou de ressources supplémentaires; nous pouvons librement les ajouter, les supprimer et les renommer sans perdre de données.\n\nPour savoir quels sont les étudiants d'un enseignant spécifique, nous définissons donc un lien retour:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nIl faut préciser le lien vers lequel pointe le backlink. Il est possible d'avoir plusieurs liens différents entre deux objets.\n\n## Initialisation des liens\n\n`IsarLink` et `IsarLinks` ont un constructeur sans argument, qui devrait être utilisé pour assigner la propriété de lien quand l'objet est créé. C'est une bonne pratique de rendre les propriétés de lien `final`.\n\nLorsque nous sauvegardons (`put()`) notre objet pour la première fois, le lien est initialisé avec la collection source et cible, et nous pouvons appeler des méthodes comme `load()` et `save()`. Un lien commence à suivre les changements immédiatement après sa création, donc nous pouvons ajouter et supprimer des relations avant même que le lien soit initialisé.\n\n:::danger\nIl est illégal de déplacer un lien vers un autre objet.\n:::\n"
  },
  {
    "path": "docs/docs/fr/queries.md",
    "content": "---\ntitle: Requêtes\n---\n\n# Requêtes\n\nLes requêtes nous permettent de trouver des enregistrements correspondant à certaines conditions, par exemple:\n\n- Trouver tous les contacts favoris.\n- Trouver des prénoms distincts dans les contacts.\n- Supprimez tous les contacts dont le nom de famille n'est pas défini.\n\nComme les requêtes sont exécutées sur la base de données et non dans Dart, elles sont très rapides. Si nous utilisons intelligemment les index, nous pouvons encore améliorer les performances des requêtes. Dans ce qui suit, nous apprendrons comment écrire des requêtes et comment les rendre le plus rapide possible.\n\nIl existe deux méthodes différentes pour filtrer les enregistrements: les filtres et les indexes. Nous allons commencer par examiner le fonctionnement des filtres.\n\n## Filtres\n\nLes filtres sont faciles à utiliser et à comprendre. Selon le type des champs, il existe différentes opérations de filtrage disponibles, dont la plupart ont des noms explicites.\n\nLes filtres fonctionnent en évaluant une expression pour chaque objet de la collection à filtrer. Si l'expression donne un résultat \"vrai\" (`true`), Isar l'inclura dans les résultats. Les filtres n'affectent pas l'ordre des résultats.\n\nNous utiliserons le modèle suivant pour les exemples ci-dessous:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### Conditions de requête\n\nSelon le type de champ, il existe différentes conditions.\n\n| Condition                | Description                                                                                                                                                           |\n|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `.equalTo(value)`        | Recherche les valeurs qui sont égales à `value`.                                                                                                                      |\n| `.between(lower, upper)` | Recherche les valeurs qui se situent entre `lower` et `upper`.                                                                                                        |\n| `.greaterThan(bound)`    | Recherche les valeurs qui sont supérieures à `bound`.                                                                                                                 |\n| `.lessThan(bound)`       | Recherche les valeurs qui sont inférieures à `bound`. Les valeurs `null` seront incluses par défaut car `null` est considéré comme plus petit que toute autre valeur. |\n| `.isNull()`              | Recherche les valeurs qui sont `null`.                                                                                                                                |\n| `.isNotNull()`           | Recherche les valeurs qui ne sont pas `null`.                                                                                                                         |\n| `.length()`              | Les requêtes sur la longueur des listes, Strings et liens filtrent les objets en fonction du nombre d'éléments dans une liste ou un lien.                             |\n\nSupposons que la base de données contienne quatre chaussures de tailles 39, 40, 46 et une de taille non définie (`null`). Si nous n'effectuons pas de tri, les valeurs seront retournées trier par id.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### Opérateurs logiques\n\nNous pouvons composer des prédicats à l'aide des opérateurs logiques suivants:\n\n| Opérateur  | Description                                                                  |\n|------------|------------------------------------------------------------------------------|\n| `.and()`   | Évalue à `true` si les expressions de gauche et de droite évaluent à `true`. |\n| `.or()`    | Évalue à `true` si l'une des deux expressions évalue à `true`.               |\n| `.xor()`   | Évalue à `true` si exactement une expression évalue à `true`.                |\n| `.not()`   | Négativise le résultat de l'expression suivante.                             |\n| `.group()` | Regroupe les conditions et permet de spécifier l'ordre d'évaluation.         |\n\nSi nous voulons trouver toutes les chaussures de taille 46, nous pouvons utiliser la requête suivante:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nSi nous voulons utiliser plus d'une condition, nous pouvons combiner plusieurs filtres à l'aide du **et** (`.and()`) logique, **ou** (`.or()`) logique et **xor** (`.xor()`) logique.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Facultatif. Les filtres sont implicitement combinés avec des et logiques.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nCette requête est équivalente à `size == 46 && isUnisex == true`.\n\nNous pouvons également regrouper des conditions en utilisant `.group()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nCette requête est équivalente à `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nPour nier une condition ou un groupe, utilisons l’opérateur logique **not** (`.not()`):\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nCette requête est équivalente à `size != 46 && isUnisex != true`.\n\n### Conditions de chaîne de caractères\n\nEn plus des conditions de recherche ci-dessus, les valeurs de type `String` offrent quelques conditions supplémentaires que nous pouvons utiliser. Les caractères génériques de type Regex, par exemple, permettent une plus grande flexibilité dans la recherche.\n\n| Condition            | Description                                                           |\n|----------------------|-----------------------------------------------------------------------|\n| `.startsWith(value)` | Recherche les valeurs qui commencent par la valeur `value` fournie.   |\n| `.contains(value)`   | Recherche les valeurs qui contiennent la valeur `value` fournie.      |\n| `.endsWith(value)`   | Recherche les valeurs qui se terminent par la valeur `value` fournie. |\n| `.matches(wildcard)` | Recherche les valeurs qui correspondent au motif `wildcard` fourni.   |\n\n**Sensibilité à la casse**  \nToutes les opérations sur les chaînes de caractères ont un paramètre optionnel `caseSensitive` dont la valeur par défaut est `true`.\n\n**Motifs:**  \nUne [expression de métacaractère](https://fr.wikipedia.org/wiki/M%C3%A9tacaract%C3%A8re) est une chaîne de caractères qui utilise des caractères normaux avec deux caractères génériques spéciaux:\n\n- Le caractère générique `*` correspond à zéro ou plus de n'importe quel caractère.\n- Le caractère générique `?` correspond à n'importe quel caractère.\n  Par exemple, la chaîne générique `\"d?g\"` correspond à `\"dog\"`, `\"dig\"` et `\"dug\"`, mais pas à `\"ding\"`, `\"dg\"` ou `\"a dog\"`.\n\n### Modificateurs de requête\n\nIl est parfois nécessaire de construire une requête basée sur certaines conditions ou pour différentes valeurs. Isar dispose d'un outil très puissant pour construire des requêtes conditionnelles:\n\n| Modificateur          | Description                                                                                                                                                                           |\n|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `.optional(cond, qb)` | Étend la requête uniquement si la `condition` est `true`. Cela peut être utilisé presque partout dans une requête, par exemple pour la trier ou la limiter de manière conditionnelle. |\n| `.anyOf(list, qb)`    | Étend la requête pour chaque valeur de `values` et combine les conditions en utilisant l’opérateur **ou**.                                                                            |\n| `.allOf(list, qb)`    | Étend la requête pour chaque valeur de `values` et combine les conditions en utilisant les **et** logiques.                                                                           |\n\nDans cet exemple, nous construisons une méthode qui trouve des chaussures avec un filtre optionnel:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // Seulement appliquer le filtre si sizeFilter != null\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nSi nous voulons trouver toutes les chaussures qui ont une ou plusieurs tailles, nous pouvons soit écrire une requête classique, soit utiliser le modificateur `anyOf()`:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nLes modificateurs de requête sont particulièrement utiles lorsque nous souhaitons construire des requêtes dynamiques.\n\n### Listes\n\nMême les listes peuvent être utilisées dans les requêtes:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nNous pouvons effectuer des requêtes en fonction de la longueur de la liste:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nCes requêtes sont équivalentes au code Dart `tweets.where((t) => t.hashtags.isEmpty)` et `tweets.where((t) => t.hashtags.length > 5)`. Nous pouvons également effectuer des requêtes sur les éléments de la liste:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nCette requête est équivalente au code Dart `tweets.where((t) => t.hashtags.contains('flutter'))`.\n\n### Objets embarqués\n\nLes objets embarqués sont l'une des fonctionnalités les plus utiles d'Isar. Ils peuvent être filtrés très efficacement en utilisant les mêmes conditions que celles disponibles pour les objets de niveau supérieur. Supposons que nous ayons le modèle suivant:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nNous voulons filtrer toutes les voitures qui ont une marque avec le nom `\"BMW\"` et le pays `\"Allemagne\"`. Nous pouvons le faire en utilisant la requête suivante:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nEssayez toujours de regrouper les requêtes imbriquées. La requête ci-dessus est plus efficace que la suivante, même si le résultat est le même:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### Liens\n\nSi nos modèles contiennent des [liens](links), nous pouvons filtrer sur les objets liés ou le nombre d'objets liés.\n\n:::warning\nGardez en tête que les requêtes de liens peuvent être coûteuses, car Isar doit rechercher les objets liés. Pensez à utiliser des objets embarqués à la place.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nNous voulons trouver tous les élèves qui ont un professeur de mathématiques ou d'anglais:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nLes filtres de liens sont évalués à `true` si au moins un objet lié correspond aux conditions.\n\nCherchons tous les élèves qui n'ont pas de professeur:\n  \n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\nou sinon:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Clauses `Where`\n\nLes clauses `where` sont un outil très puissant, mais il n'est pas toujours facile de les utiliser correctement.\n\nContrairement aux filtres, les clauses `where` utilisent les index que nous définissons dans le schéma pour évaluer les conditions de la requête. La requête d'un index est beaucoup plus rapide que le filtrage individuel de chaque entrée.\n\n➡️ En savoir plus: [Indices](indexes)\n\n:::tip\nEn règle générale, vous devriez toujours essayer de réduire les entrées autant que possible à l'aide de clauses `where`, et effectuer le reste du filtrage à l'aide de filtres.\n:::\n\nNous pouvons uniquement combiner les clauses `where` en utilisant des **ou** logiques. En d'autres termes, nous pouvons additionner plusieurs clauses `where`, mais nous ne pouvons pas effectuer une requête sur l'intersection de plusieurs clauses `where`.\n\nAjoutons des index à la collection `Shoe`:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nIl y a deux index. L'index sur `size` nous permet d'utiliser des clauses `where` comme `.sizeEqualTo()`. L'index composé sur `isUnisex` permet d'utiliser des clauses `where` comme `isUnisexSizeEqualTo()`, mais aussi `isUnisexEqualTo()`, car nous pouvons toujours utiliser n'importe quel préfixe d'un index.\n\nNous pouvons maintenant réécrire la requête précédente qui trouve des chaussures unisexes de taille 46 en utilisant l'index composé. Cette requête sera beaucoup plus rapide que la précédente:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nLes clauses `where` ont deux autres superpouvoirs: elles vous offrent un tri \"gratuit\" et une opération distincte super rapide.\n\n### Combinaison de clauses `where` et de filtres\n\nVous vous souvenez des requêtes `shoes.filter()`? Il s'agit en fait d'un raccourci pour `shoes.where().filter()`. Nous pouvons (et devrions) combiner les clauses `where` et les filtres dans une même requête pour bénéficier des avantages des deux:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nLa clause `where` est d'abord appliquée pour réduire le nombre d'objets à filtrer. Ensuite, le filtre est appliqué aux objets restants.\n\n## Triage\n\nNous pouvons définir comment les résultats doivent être triés lors de l'exécution de la requête en utilisant les méthodes `.sortBy()`, `.sortByDesc()`, `.thenBy()` et `.thenByDesc()`.\n\nPour trouver toutes les chaussures triées par nom de modèle en ordre croissant et par taille en ordre décroissant sans utiliser d'index:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nLe tri de nombreux résultats peut s'avérer coûteux, d'autant plus que le tri intervient avant le `offset` et `limit`. Les méthodes de tri ci-dessus ne font jamais appel aux index. Heureusement, nous pouvons à nouveau utiliser le tri par clause `where` et rendre notre requête rapide comme l'éclair, même si nous devons trier un million d'objets.\n\n### Tri par clause `where`\n\nSi nous utilisons une clause `where` **simple** dans notre requête, les résultats sont déjà triés par l'index. Ce n'est pas rien!\n\nSupposons que nous avons des chaussures de taille `[43, 39, 48, 40, 42, 45]` et que nous voulons trouver toutes les chaussures dont la taille est supérieure à `42` et les trier par taille:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // Trie également les résultats par taille\n  .findAll(); // -> [43, 45, 48]\n```\n\nComme nous pouvons le constater, le résultat est trié par l'index `size`. Si nous voulons inverser l'ordre de tri de la clause `where`, nous pouvons donner à `sort` la valeur `Sort.desc` :\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nParfois, nous ne voulons pas utiliser des clauses `where`, mais nous pouvons tout de même bénéficier du tri implicite. Nous pouvons utiliser la clause `where` `any`:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nSi nous utilisons un index composé, les résultats sont triés par tous les champs de l'index.\n\n:::tip\nSi vous avez besoin que les résultats soient triés, pensez à utiliser un index dans ce but. Surtout si vous utilisez avec `offset()` et `limit()`.\n:::\n\nParfois, il n'est pas possible ou utile d'utiliser un index pour le tri. Dans ce cas, nous devons utiliser des index pour réduire autant que possible le nombre d'entrées résultantes.\n\n## Valuers uniques\n\nPour ne renvoyer que les entrées ayant des valeurs uniques, utilisez le prédicat `distinct`. Par exemple, pour savoir combien de modèles de chaussures différents nous avons dans votre base de données Isar:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nNous pouvons également chaîner plusieurs conditions distinctes pour trouver toutes les chaussures avec des combinaisons modèle-taille distinctes:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nSeul le premier résultat de chaque combinaison distincte est retourné. Nous pouvons utiliser des clauses `where` et des opérations de tri pour le contrôler.\n\n### Clause `where` distincte\n\nSi nous avons un index non-unique, nous pouvons vouloir obtenir toutes ses valeurs distinctes. Nous pouvons utiliser l'opération `distinctBy` de la section précédente, mais elle est effectuée après le tri et les filtres, ce qui entraîne une certaine lourdeur.  \nSi nous n'utilisons qu'une seule clause `where`, nous pouvons nous fier à l'index pour effectuer l'opération de distinction.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nEn théorie, nous pouvons même utiliser plusieurs clauses `where` pour le tri et la distinction. La seule restriction est que ces clauses `where` ne doivent pas se chevaucher et utiliser le même index. Pour un tri correct, elles doivent également être appliquées dans l'ordre de tri. Soyez très prudent si vous vous fiez à cela!\n:::\n\n## Décalage et limite\n\nC'est souvent une bonne idée de limiter le nombre de résultats d'une requête pour les listes \"lazy\". Nous pouvons le faire en définissant un `limit()`:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nEn définissant un `offset()`, nous pouvons également paginer les résultats de notre requête.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nL'instanciation des objets Dart étant souvent la partie la plus coûteuse de l'exécution d'une requête, il est judicieux de ne charger que les objets dont nous avons de besoin.\n\n## Ordre d'exécution\n\nIsar exécute les requêtes toujours dans le même ordre :\n\n1. Traverser l'index primaire ou secondaire pour trouver des objets (appliquer des clauses `where`)\n2. Filtrer les objets\n3. Trier les résultats\n4. Appliquer l'opération distincte\n5. Décalage et limite des résultats\n6. Retour des résultats\n\n## Opérations de requêtes\n\nDans les exemples précédents, nous avons utilisé `.findAll()` pour récupérer tous les objets correspondants. Cependant, d'autres opérations sont disponibles:\n\n| Opération        | Description                                                                                                                                                   |\n|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `.findFirst()`   | Retourne seulement le premier objet correspondant ou `null` si aucun ne correspond.                                                                           |\n| `.findAll()`     | Retourne tous les objets correspondants.                                                                                                                      |\n| `.count()`       | Compte le nombre d'objets correspondant à la requête.                                                                                                         |\n| `.deleteFirst()` | Supprime le premier objet correspondant de la collection.                                                                                                     |\n| `.deleteAll()`   | Supprime tous les objets correspondants de la collection.                                                                                                     |\n| `.build()`       | Compile la requête pour la réutiliser plus tard. Cela permet d'économiser le coût de construction d'une requête si nous souhaitons l'exécuter plusieurs fois. |\n\n## Requêtes de propriété\n\nSi nous ne sommes intéressés que par les valeurs d'une seule propriété, nous pouvons utiliser une requête de propriété. Il suffit de construire une requête ordinaire et de sélectionner une propriété:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nL'utilisation d'une seule propriété permet de gagner du temps lors de la désérialisation. Les requêtes de propriétés fonctionnent également pour les objets embarqués et les listes.\n\n## Agrégation\n\nIsar supporte l'agrégation des valeurs d'une requête de propriété. Les opérations d'agrégation disponibles sont les suivantes :\n\n| Opération    | Description                                                                |\n|--------------|----------------------------------------------------------------------------|\n| `.min()`     | Trouve la valeur minimale ou `null` si aucune ne correspond.               |\n| `.max()`     | Trouve la valeur maximale ou `null` si aucune ne correspond.               |\n| `.sum()`     | Additionne toutes les valeurs.                                             |\n| `.average()` | Calcule la moyenne de toutes les valeurs ou `NaN` si aucune ne correspond. |\n\nL'utilisation des agrégations est beaucoup plus rapide que la recherche de tous les objets correspondants et l'exécution manuelle de l'agrégation.\n\n## Requêtes dynamiques\n\n:::danger\nCette section n'est probablement pas pertinente pour vous. Il est déconseillé d'utiliser des requêtes dynamiques, sauf si vous en avez absolument besoin (ce qui est rarement le cas).\n:::\n\nTous les exemples ci-dessus ont utilisé le `QueryBuilder` et les méthodes d'extension statiques générées. Peut-être voulez-vous créer des requêtes dynamiques ou un langage de requête personnalisé (comme l'inspecteur Isar). Dans ce cas, nous pouvons utiliser la méthode `buildQuery()` :\n\n| Paramètre       | Description                                                                                                          |\n|-----------------|----------------------------------------------------------------------------------------------------------------------|\n| `whereClauses`  | Les clauses `where` de la requête.                                                                                   |\n| `whereDistinct` | Si les clauses `where` doivent retourner des valeurs distinctes (utile uniquement pour les clauses `where` uniques). |\n| `whereSort`     | L'ordre de passage des clauses `where` (utile uniquement pour les clauses `where` uniques).                          |\n| `filter`        | Le filtre à appliquer aux résultats.                                                                                 |\n| `sortBy`        | Une liste de propriétés à trier.                                                                                     |\n| `distinctBy`    | Une liste de propriétés à distinguer par.                                                                            |\n| `offset`        | Le décalage des résultats.                                                                                           |\n| `limit`         | Le nombre maximum de résultats à retourner.                                                                          |\n| `property`      | Si non-nulle, seules les valeurs de cette propriété sont renvoyées.                                                  |\n\nCréons une requête dynamique:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nLa requête suivante est équivalente:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/fr/recipes/data_migration.md",
    "content": "---\ntitle: Migration des données\n---\n\n# Migration des données\n\nIsar migre automatiquement les schémas de notre base de données si nous ajoutons ou supprimons des collections, champs ou index. Il peut arriver que nous souhaitions également migrer des données. Isar n'offre pas de solution intégrée, car cela imposerait des restrictions de migration arbitraires. Il est facile d'implémenter une logique de migration adaptée à nos besoins.\n\nDans cet exemple, nous voulons utiliser une seule version pour l'ensemble de la base de données. Nous utilisons `shared_preferences` pour stocker la version actuelle et la comparer à la version vers laquelle nous désirons migrer. Si les versions ne correspondent pas, nous migrons les données et mettons à jour la version.\n\n:::tip\nVous pouvez également donner à chaque collection sa propre version et les migrer individuellement.\n:::\n\nImaginons que nous avons une collection d'utilisateurs avec un champ d'anniversaire. Dans la version 2 de notre application, nous avons besoin d'un champ supplémentaire pour l'année de naissance afin de rechercher des utilisateurs en fonction de leur âge.\n\nVersion 1:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersion 2:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\nLe problème est que les modèles d'utilisateurs existants auront un champ `birthYear` vide, car il n'existait pas dans la version 1. Nous devons migrer les données pour définir le champ `birthYear`.\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // Si la version n'est pas définie (nouvelle installation) ou si elle est déjà à 2, il n'est pas nécessaire de migrer.\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // Mise à jour de la version\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // Nous paginons à travers les utilisateurs pour éviter de tous les charger en mémoire en même temps\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // Nous n'avons pas besoin de mettre à jour quoi que ce soit puisque le `getter` `birthYear` est utilisé.\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\nSi vous devez migrer un grand nombre de données, envisagez d'utiliser un isolat en arrière plan pour éviter de surcharger le thread de l'interface utilisateur.\n:::\n"
  },
  {
    "path": "docs/docs/fr/recipes/full_text_search.md",
    "content": "---\ntitle: Recherche plein texte\n---\n\n# Recherche plein texte\n\nLa recherche plein texte est un moyen puissant de rechercher du texte dans la base de données. Vous devriez déjà être familiarisé avec le fonctionnement des [indices](../indexes), mais passons en revue les principes de base.\n\nUn index fonctionne comme une table de recherche, permettant au moteur de recherche de trouver rapidement les enregistrements ayant une valeur donnée. Par exemple, si nous avons un champ \"titre\" dans notre objet, nous pouvons créer un index sur ce champ afin de trouver plus rapidement les objets ayant un titre donné.\n\n## Pourquoi la recherche plein texte est-elle utile?\n\nNous pouvons facilement rechercher du texte en utilisant des filtres. Il existe plusieurs opérations de chaînes de caractères, par exemple `.startsWith()`, `.contains()` et `.matches()`. Le problème avec les filtres est que leur temps d'exécution est de `O(n)`, où `n` est le nombre d'enregistrements dans la collection. Les opérations sur chaînes de caractères comme `.matches()` sont particulièrement coûteuses.\n\n:::tip\nLa recherche plein texte est beaucoup plus rapide que les filtres, mais les index ont certaines limites. Dans cette recette, nous allons explorer comment contourner ces limites.\n:::\n\n## Exemple de base\n\nL'idée est toujours la même: au lieu d'indexer l'ensemble du texte, nous indexons les mots du texte afin de pouvoir les rechercher individuellement.\n\nCréons l'index plein texte le plus basique:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nNous pouvons maintenant rechercher des messages dont le contenu contient des mots spécifiques:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nCette requête est super rapide, mais il y a quelques problèmes:\n\n1. Nous ne pouvons rechercher que des mots entiers\n2. Nous ne tenons pas compte de la ponctuation\n3. Nous ne prenons pas en charge les autres caractères d'espacement\n\n## Diviser le texte de la bonne manière\n\nEssayons d'améliorer l'exemple précédent. Nous pourrions essayer de développer une regex compliquée pour corriger le découpage de mots, mais cela sera probablement lent et incorrect dans certains cas.\n\nLe [Unicode Annex #29](https://unicode.org/reports/tr29/) définit comment diviser correctement un texte en mots pour presque toutes les langues. C'est assez compliqué, mais heureusement, Isar fait le gros du travail pour nous:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## Je veux plus de contrôle\n\nC'est simple et facile! Nous pouvons également modifier notre index pour supporter la comparaison des préfixes et la correspondance insensible à la casse:\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nPar défaut, Isar stocke les mots sous forme de valeurs hachées, ce qui est rapide et peu encombrant. Mais les valeurs hachées ne peuvent pas être utilisées pour la comparaison des préfixes. En utilisant `IndexType.value`, nous pouvons changer l'index pour utiliser directement les mots à la place. Cela nous donne la clause `where` `.titleWordsAnyStartsWith()`:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## Je veux aussi `.endsWith()`\n\nBien sûr! Nous allons utiliser une astuce pour réaliser la comparaison `.endsWith()`:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nN'oublions pas d'inverser la terminaison que nous voulons rechercher:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Algorithmes de racinisation (`stemming`)\n\nMalheureusement, les index ne supportent pas la comparaison `.contains()` (ceci est vrai pour d'autres bases de données également). Mais il y a quelques alternatives qui valent la peine d'être explorées. Le choix dépend fortement de votre utilisation. Un exemple est l'indexation des racines de mots au lieu du mot entier.\n\nUn algorithme de racinisation est un processus de normalisation linguistique dans lequel les différentes formes d'un mot sont réduites à une forme commune :\n\n```\nconnexion\nconnexions\nconnectif          --->   connect\nconnecté\nconnecter\n```\n\nLes algorithmes les plus populaires sont [Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) et [Snowball stemming algorithms](https://snowballstem.org/algorithms/).\n\nIl existe également des formes plus avancées comme la [Lemmatisation](https://fr.wikipedia.org/wiki/Lemmatisation).\n\n## Algorithmes phonétiques\n\nUn [algorithme phonétique](https://fr.wikipedia.org/wiki/Algorithme_phon%C3%A9tique) est un algorithme permettant d'indexer les mots en fonction de leur prononciation. En d'autres termes, il nous permet de trouver des mots dont la sonorité est similaire à celle des mots que nous voulons recherchez.\n\n:::warning\nLa plupart des algorithmes phonétiques ne supportent qu'une seule langue.\n:::\n\n### Soundex\n\n[Soundex](https://fr.wikipedia.org/wiki/Soundex) est un algorithme phonétique d'indexation des noms par le son, tel qu'il est prononcé en anglais. Le but est que les homophones soient encodés dans la même représentation, afin qu'ils puissent être mis en relation malgré des différences mineures dans l'orthographe. Il s'agit d'un algorithme simple, et il existe de nombreuses versions améliorées.\n\nEn utilisant cet algorithme, `\"Robert\"` et `\"Rupert\"` renvoient tous deux la chaîne `\"R163\"`, tandis que `\"Rubin\"` donne `\"R150\"`. `\"Ashcraft\"` et `\"Ashcroft\"` donnent tous deux `\"A261\"`.\n\n### Double Metaphone\n\nL'algorithme de codage phonétique [Double Metaphone](https://fr.wikipedia.org/wiki/Metaphone) est la deuxième génération de cet algorithme. Il apporte plusieurs améliorations fondamentales à la conception de l'algorithme Metaphone original.\n\nDouble Metaphone prend en compte diverses irrégularités de l'anglais d'origine slave, germanique, celtique, grecque, française, italienne, espagnole, chinoise et autres.\n"
  },
  {
    "path": "docs/docs/fr/recipes/multi_isolate.md",
    "content": "---\ntitle: Utilisation multi-isolats\n---\n\n# Utilisation multi-isolats\n\nAu lieu de threads, tout code Dart s'exécute dans des isolats. Chaque isolat possède son propre espace mémoire, ce qui garantit qu'aucun des états d'un isolat n'est accessible depuis un autre isolat.\n\nIl est possible d'accéder à Isar à partir de plusieurs isolats en même temps. Même les observateurs fonctionnent à travers les isolats. Dans cette recette, nous allons voir comment utiliser Isar dans un environnement multi-isolats.\n\n## Quand utiliser plusieurs isolats\n\nLes transactions Isar sont exécutées en parallèle, même si elles sont exécutées dans le même isolat. Dans certains cas, il est toujours utile d'accéder à Isar à partir de plusieurs isolats.\n\nLa raison en est qu'Isar passe un certain temps à encoder et décoder des données depuis et vers des objets Dart. Nous pouvons imaginer que c'est comme coder et décoder en JSON (en plus efficace). Ces opérations s'exécutent à l'intérieur de l'isolat à partir duquel on accède aux données et bloquent naturellement les autres codes de l'isolat. En d'autres termes: Isar effectue une partie du travail dans votre isolat Dart.\n\nSi nous n'avons besoin de lire ou d'écrire que quelques centaines d'objets à la fois, le faire dans l'isolat de l'interface utilisateur ne pose pas de problème. Mais pour les transactions importantes ou si le thread de l'interface utilisateur est déjà occupé, nous devrions envisager d'utiliser un isolat séparé.\n\n## Exemple\n\nLa première chose que nous devons faire est d'ouvrir Isar dans le nouvel isolat. Puisque l'instance de Isar est déjà ouverte dans l'isolat principal, `Isar.open()` retournera la même instance.\n\n:::warning\nAssurez-vous de fournir les mêmes schémas que dans l'isolat principal. Sinon, vous obtiendrez une erreur.\n:::\n\n`compute()` démarre un nouvel isolat dans Flutter et y exécute la fonction donnée.\n\n```dart\nvoid main() {\n  // Ouvre Isar dans l'isolat de l'interface utilisateur\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // Écoute les changements dans la base de données\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // Démarre un nouvel isolat et crée 10000 messages\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // Après quelque temps:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// Fonction qui sera exécutée dans le nouvel isolat\nFuture createDummyMessages(int count) async {\n  // Nous n'avons pas besoin du chemin du dossier ici étant donné que l'instance est déjà ouverte.\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // Nous utilisons une transaction synchrone en isolat\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nIl y a quelques éléments intéressants à noter dans l'exemple ci-dessus:\n\n- `isar.messages.watchLazy()` est appelé dans l'isolat UI et est notifié des changements provenant d'un autre isolat.\n- Les instances sont référencées par leur nom. Le nom par défaut est `default`, mais dans cet exemple, nous l'avons défini comme `myInstance`.\n- Nous avons utilisé une transaction synchrone pour créer les mesasges. Bloquer notre nouvel isolat n'est pas un problème, et les transactions synchrones sont un peu plus rapides.\n"
  },
  {
    "path": "docs/docs/fr/recipes/string_ids.md",
    "content": "---\ntitle: Identifiants en chaîne de caractères\n---\n\n# Identifiants en chaîne de caractères\n\nC'est l'une des demandes les plus fréquemment reçues. Voici donc un tutoriel sur l'utilisation des ids en `String`.\n\nIsar ne supporte pas nativement les ids `String`, et il y a une bonne raison à cela: les ids entiers sont beaucoup plus efficaces et rapides. En particulier pour les liens, la complexité d'un identifiant de type `String` est trop importante.\n\nIl arrive parfois que l'on doive stocker des données externes qui utilisent des UUID ou autres identifiants non entiers. Il est recommandé de stocker la chaîne id comme une propriété de votre objet et d'utiliser une implémentation de hachage rapide pour générer un int 64 bits qui peut être utilisé comme Id.\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nAvec cette approche, nous obtenons le meilleur des deux mondes: des identifiants entiers efficaces pour les liens et la possibilité d'utiliser des identifiants de type `String`.\n\n## Fonction de hachage rapide\n\nIdéalement, notre fonction de hachage devrait avoir une haute qualité (nous ne voulons pas de collisions) et être rapide. Il est recommandé d'utiliser l'implémentation suivante:\n\n```dart\n/// Algorithme de hachage FNV-1a 64 bits optimisé pour les chaînes de caractères Dart\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nSi vous choisissez une fonction de hachage différente, assurez-vous qu'elle renvoie un int 64 bits et évitez d'utiliser une fonction de hachage cryptographique, car elle est beaucoup plus lente.\n\n:::warning\nÉvitez d'utiliser `string.hashCode`, car sa stabilité n'est pas garantie sur les différentes plateformes et versions de Dart.\n:::"
  },
  {
    "path": "docs/docs/fr/schema.md",
    "content": "---\ntitle: Schéma\n---\n\n# Schéma\n\nLorsque vous utilisez Isar pour stocker les données de votre application, vous devez utiliser des collections. Une collection est comme une table de base de données, et ne peut contenir qu'un seul type d'objet Dart. Chaque objet de collection représente une entrée de données dans la collection correspondante.\n\nLa définition d'une collection s'appelle \"schéma\". Le générateur Isar fera le gros du travail pour nous et générera la plupart du code dont nous avons besoin pour utiliser la collection.\n\n## Anatomie d'une collection\n\nNous définissons chaque collection Isar en annotant une classe avec `@collection` ou `@Collection()`. Une collection Isar comprend des champs pour chaque colonne de la table correspondante dans la base de données, y compris un champ qui comprend la clé primaire.\n\nLe code suivant est un exemple d'une collection simple qui définit une table `User` avec des colonnes pour l'ID, le prénom et le nom :\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nPour faire persister un champ, Isar doit y avoir accès. Vous pouvez vous assurer que Isar y a accès en le rendant public ou en fournissant des méthodes `getter` et `setter`.\n:::\n\nIl existe quelques paramètres facultatifs permettant de personnaliser la collection:\n\n| Config        | Description                                                                                                         |\n| ------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Contrôle si les champs des classes parentes et des mixins seront stockés dans Isar. Activé par défaut.              |\n| `accessor`    | Permet de renommer l'accesseur de collection par défaut (par exemple `isar.contacts` pour la collection `Contact`). |\n| `ignore`      | Permet d'ignorer certaines propriétés de la classe. Celles-ci sont également respectées pour les classes parentes.  |\n\n### Id Isar\n\nChaque classe de collection doit définir une propriété id de type `Id`, qui identifie de façon unique un objet. `Id` est un alias pour `int` qui permet au générateur Isar de reconnaître la propriété id.\n\nIsar indexe automatiquement les champs id, ce qui nous permet d'obtenir et de modifier les objets en fonction de leur id de manière efficace.\n\nVous pouvez soit définir les ids vous-même, soit demander à Isar d'attribuer un id auto-incrémenté. Si le champ `id` est `null` et non `final`, Isar assignera un id auto-incrémenté. Si vous voulez un identifiant auto-incrémenté non nul, vous pouvez utiliser `Isar.autoIncrement` au lieu de `null`.\n\n:::tip\nLes identifiants d'auto-incrémentation ne sont pas réutilisés lorsqu'un objet est supprimé. La seule façon de réinitialiser les identifiants d'auto-incrémentation est d'effacer la collection ou la base de données.\n:::\n\n### Renommer les collections et champs\n\nPar défaut, Isar utilise le nom de la classe comme nom de collection. De même, Isar utilise les noms de champs comme noms de colonnes dans la base de données. Si vous voulez qu'une collection ou un champ ait un nom différent, ajoutez l'annotation `@Name()`. L'exemple suivant montre des noms personnalisés pour les collections et les champs :\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nVous devriez envisager d'utiliser l'annotation `@Name()` si vous voulez renommer des champs ou des classes Dart qui sont déjà stockés dans la base de données. Sinon, la base de données supprimera et recréera le champ ou la collection.\n\n### Ignorer des champs\n\nIsar persiste tous les champs publics d'une classe de collection. En annotant une propriété ou un `getter` avec `@ignore`, vous pouvez l'exclure de la persistance, comme le montre l'extrait de code suivant:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nDans les cas où une collection hérite de champs d'une collection parente, il est généralement plus facile d'utiliser la propriété `ignore` de l'annotation `@Collection`:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nSi une collection contient un champ dont le type n'est pas supporté par Isar, vous devez ignorer ce champ.\n\n:::warning\nGardez en tête qu'il n'est pas recommandé de stocker des informations dans des objets Isar qui ne sont pas persistants.\n:::\n\n## Types supportés\n\nIsar supporte les types de données suivants :\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nDe plus, les objets embarqués (`embedded`) et les enums sont supportés. Nous les aborderons ci-dessous.\n\n## byte, short, float\n\nPour de nombreux cas d'utilisation, vous n'avez pas besoin de l'étendue complète d'un nombre entier ou double de 64 bits. Isar supporte des types supplémentaires qui vous permettent d'économiser de l'espace et de la mémoire lorsque vous stockez des nombres plus petits.\n\n| Type       | Size in bytes | Range                                                  |\n| ---------- | ------------- | ------------------------------------------------------ |\n| **byte**   | 1             | 0 à 255                                                |\n| **short**  | 4             | -2,147,483,647 à 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 à 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 à 3.4e38                                       |\n| **double** | 8             | -1.7e308 à 1.7e308                                     |\n\nLes types supplémentaires sont simplement des alias pour les types natifs de Dart, donc utiliser `short`, par exemple, fonctionne de la même manière que `int`.\n\nVoici un exemple de collection contenant les types décrit ci-dessus:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nTous les types de nombres peuvent également être utilisés dans des listes. Pour stocker des octets (`bytes`), vous devriez utiliser `List<byte>`.\n\n## Types nullables\n\nIl est essentiel de comprendre comment la nullité fonctionne dans Isar: Les types de nombres n'ont **PAS** de représentation `null` dédiée. À la place, une valeur spécifique est utilisée:\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    | `int.MIN`     |\n| **float**  | `double.NaN`  |\n| **double** | `double.NaN`  |\n\n`bool`, `String`, et `List` ont une représentation `null` séparée.\n\nCe comportement permet d'améliorer les performances, et il vous permet de modifier librement la nullité de vos champs sans nécessiter de migration ou de code spécial pour gérer les valeurs \"nulles\".\n\n:::warning\nLe type `byte` ne supporte pas les valeurs nulles.\n:::\n\n## DateTime\n\nIsar ne stocke pas les informations de fuseau horaire de vos dates. À la place, il les convertit en UTC avant de les stocker. Isar retourne toutes les dates en heure locale.\n\nLes `DateTime` sont stockés avec une précision de l'ordre de la microseconde. Dans les navigateurs, seule la précision de la milliseconde est supportée en raison des limitations de JavaScript.\n\n## Enum\n\nIsar permet de stocker et d'utiliser les enums comme tous les autres types Isar. Vous devez cependant choisir comment Isar doit représenter l'enum sur disque. Isar supporte quatre stratégies différentes :\n\n| EnumType    | Description                                                                                      |\n| ----------- | ------------------------------------------------------------------------------------------------ |\n| `ordinal`   | L'index de l'enum est stocké comme `byte`. Très efficace mais ne permet pas les enums nullables. |\n| `ordinal32` | L'index de l'enum est stocké comme `short` (entier de 4 octets).                                 |\n| `name`      | Le nom de l'enum est stocké comme `String`.                                                      |\n| `value`     | Une propriété personnalisée est utilisée pour récupérer la valeur de l'enum.                     |\n\n:::warning\n`ordinal` et `ordinal32` dépendent de l'ordre des valeurs de l'enum. Si vous changez l'ordre, les bases de données existantes renverront des valeurs incorrectes.\n:::\n\nVoici un exemple pour chaque stratégie:\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // Même chose que EnumType.ordinal\n  late TestEnum byteIndex; // Ne peut pas être nulle\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // Ne peut pas être nulle\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nBien entendu, les enums peuvent également être utilisés dans des listes.\n\n## Objets embarqués\n\nIl est souvent utile d'avoir des objets imbriqués dans votre modèle de collection. Il n'y a pas de limite à la profondeur à laquelle vous pouvez imbriquer des objets. Gardez cependant à l'esprit que la mise à jour d'un objet profondément imbriqué nécessitera l'écriture de l'arbre d'objets complet dans la base de données.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nLes objets embarqués peuvent être nullables et hériter d'autres objets. La seule condition est qu'ils soient annotés avec `@embedded` et qu'ils aient un constructeur par défaut sans paramètres requis.\n"
  },
  {
    "path": "docs/docs/fr/transactions.md",
    "content": "---\ntitle: Transactions\n---\n\n# Transactions\n\nDans Isar, les transactions combinent plusieurs opérations de base de données en une seule unité de travail. La plupart des interactions avec Isar utilisent implicitement des transactions. L'accès en lecture et en écriture dans Isar est conforme à la norme [ACID](http://en.wikipedia.org/wiki/ACID). Les transactions sont automatiquement annulées en cas d'erreur.\n\n## Transactions explicites\n\nDans une transaction explicite, vous obtenez un instantané cohérent de la base de données. Essayez de minimiser la durée des transactions. Il est interdit d'effectuer des appels réseau ou d'autres opérations de longue durée dans une transaction.\n\nLes transactions (en particulier les transactions d'écriture) ont un coût, et nous devrions toujours essayer de regrouper les opérations successives en une seule transaction.\n\nLes transactions peuvent être soit synchrones ou asynchrones. Dans les transactions synchrones, nous ne pouvons utiliser que les opérations synchrones. Dans les transactions asynchrones, uniquement les opérations asynchrones.\n\n|             | Lecture      | Lecture et écriture |\n|-------------|--------------|---------------------|\n| Synchrones  | `.txnSync()` | `.writeTxnSync()`   |\n| Asynchrones | `.txn()`     | `.writeTxn()`       |\n\n\n### Transactions de lecture\n\nLes transactions de lecture explicites sont facultatives, mais elles nous permettent d'effectuer des lectures atomiques et de compter sur un état cohérent de la base de données à l'intérieur de la transaction. À l'interne, Isar utilise toujours des transactions de lecture implicites pour toutes les opérations de lecture.\n\n:::tip\nLes transactions de lecture asynchrones s'exécutent en parallèle avec d'autres transactions de lecture et d'écriture. Plutôt cool, non?\n:::\n\n### Transactions d'écriture\n\nContrairement aux opérations de lecture, les opérations d'écriture dans Isar doivent être enveloppées dans une transaction explicite.\n\nLorsqu'une transaction d'écriture se termine avec succès, elle est automatiquement validée et toutes les modifications sont écrites sur disque. Si une erreur se produit, la transaction est abandonnée et toutes les modifications sont annulées. Les transactions sont \"tout ou rien\": soit toutes les écritures d'une transaction réussissent, soit aucune d'entre elles ne prend effet pour garantir la cohérence des données.\n\n:::warning\nLorsqu'une opération de base de données échoue, la transaction est interrompue et ne doit plus être utilisée. Même si vous attrapez l'erreur dans Dart.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// BON\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// MAUVAIS : déplacer la boucle à l'intérieur de la transaction\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/fr/tutorials/quickstart.md",
    "content": "---\ntitle: Démarrage rapide\n---\n\n# Démarrage rapide\n\nVous revoilà! Commençons à utiliser la base de données Flutter la plus cool qui soit...\n\nNous allons être brefs en mots et rapides en code dans ce démarrage rapide.\n\n## 1. Ajout des dépendances\n\nAvant de débuter, nous devons ajouter quelques dépendances au fichier `pubspec.yaml`. Nous pouvons utiliser la commande `pub` pour faire le gros du travail à notre place.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. Annotation de classes\n\nAnnotez vos classes de collection avec `@collection` et choisissez un champ `Id`.\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // Vous pouvez aussi utiliser id = null pour l'auto incrémentation\n\n  String? name;\n\n  int? age;\n}\n```\n\nLes Ids identifient de manière unique les objets d'une collection et vous permettent de les retrouver ultérieurement.\n\n## 3. Exécuter le générateur de code\n\nExécutez la commande suivante pour démarrer le `build_runner`:\n\n```sh\ndart run build_runner build\n```\n\nSi vous utilisez Flutter:\n\n```sh\nflutter pub run build_runner build\n```\n\n## 4. Ouverture l'instance Isar\n\nOuvrez une nouvelle instance d'Isar et passez tous vos schémas de collection. En option, vous pouvez spécifier un nom d'instance et un dossier.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. Écriture et lecture\n\nUne fois que votre instance est ouverte, vous pouvez commencer à utiliser les collections.\n\nToutes les opérations CRUD de base sont disponibles via `IsarCollection`.\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // Insertion & modification\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // Obtention\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // Suppression\n});\n```\n\n## Autre ressources\n\nVous apprenez mieux visuellement ? Regardez ces vidéos pour commencer avec Isar:\n\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/fr/watchers.md",
    "content": "---\ntitle: Observateurs\n---\n\n# Observateurs\n\nIsar nous permet de nous abonner aux changements dans la base de données. Nous pouvons \"observer\" les modifications apportées à un objet spécifique, à une collection entière ou à une requête.\n\nLes observateurs (`Watchers`) nous permettent de réagir efficacement aux changements dans la base de données. Nous pouvons par exemple reconstruire une interface utilisateur lorsqu'un contact est ajouté, envoyer une requête réseau lorsqu'un document est mis à jour, etc.\n\nUn observateur est notifié lorsqu'une transaction est validée avec succès et que la cible est réellement modifiée.\n\n## Observation d'objets\n\nSi nous voulons être notifié lorsqu'un objet spécifique est créé, mis à jour ou supprimé, nous devons observer un objet:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\nComme nous pouvons le voir dans l'exemple ci-dessus, l'objet ne doit pas encore exister. L'observateur sera notifié lorsqu'il sera créé.\n\nIl existe un paramètre supplémentaire, `fireImmediately`. Si nous le mettons à `true`, Isar ajoutera immédiatement la valeur courante de l'objet au flux.\n\n### Observation paresseuse\n\nPeut-être n'avez-vous pas besoin de recevoir la nouvelle valeur, mais seulement d'être notifié du changement? Cela évite à Isar d'avoir à aller chercher l'objet:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## Observation de collections\n\nAu lieu d'observer un seul objet, nous pouvons observer une collection entière et être notifié lorsqu'un objet est ajouté, mis à jour ou supprimé:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## Observation de requêtes\n\nIl est même possible d'observer des requêtes entières. Isar fait de son possible pour nous notifier uniquement lorsque les résultats de la requête changent réellement. Nous ne serons pas notifiés si des liens entraînent une modification de la requête. Utilisez un observateur de collection si vous avez besoin d'être informé des changements de liens.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nSi vous utilisez des requêtes `offset`, `limit` ou `distinct`, Isar vous notifiera même si les changements y sont en dehors.\n:::\n\nTout comme `watchObject()`, nous pouvons utiliser `watchLazy()` pour être notifié lorsque les résultats de la requête changent, mais ne pas aller les chercher.\n\n:::danger\nRelancer les requêtes à chaque modification est très inefficace. Il serait préférable d'utiliser un observateur de collection paresseux (`lazy`) à la place.\n:::\n"
  },
  {
    "path": "docs/docs/indexes.md",
    "content": "---\ntitle: Indexes\n---\n\n# Indexes\n\nIndexes are Isar's most powerful feature. Many embedded databases offer \"normal\" indexes (if at all), but Isar also has composite and multi-entry indexes. Understanding how indexes work is essential to optimize query performance. Isar lets you choose which index you want to use and how you want to use it. We'll start with a quick introduction to what indexes are.\n\n## What are indexes?\n\nWhen a collection is unindexed, the order of the rows will likely not be discernible by the query as optimized in any way, and your query will therefore have to search through the objects linearly. In other words, the query will have to search through every object to find the ones matching the conditions. As you can imagine, that can take some time. Looking through every single object is not very efficient.\n\nFor example, this `Product` collection is entirely unordered.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**Data:**\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nA query that tries to find all products that cost more than €30 has to search through all nine rows. That's not an issue for nine rows, but it might become a problem for 100k rows.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nTo improve the performance of this query, we index the `price` property. An index is like a sorted lookup table:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**Generated index:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nNow, the query can be executed a lot faster. The executor can directly jump to the last three index rows and find the corresponding objects by their id.\n\n### Sorting\n\nAnother cool thing: indexes can do super fast sorting. Sorted queries are costly because the database has to load all results in memory before sorting them. Even if you specify an offset or limit, they are applied after sorting.\n\nLet's imagine we want to find the four cheapest products. We could use the following query:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nIn this example, the database would have to load all (!) objects, sort them by price, and return the four products with the lowest price.\n\nAs you can probably imagine, this can be done much more efficiently with the previous index. The database takes the first four rows of the index and returns the corresponding objects since they are already in the correct order.\n\nTo use the index for sorting, we would write the query like this:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nThe `.anyX()` where clause tells Isar to use an index just for sorting. You can also use a where clause like `.priceGreaterThan()` and get sorted results.\n\n## Unique indexes\n\nA unique index ensures the index does not contain any duplicate values. It may consist of one or multiple properties. If a unique index has one property, the values in this property will be unique. If the unique index has more than one property, the combination of values in these properties is unique.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nAny attempt to insert or update data into the unique index that causes a duplicate will result in an error:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// try to insert user with same username\nawait isar.users.put(user2); // -> error: unique constraint violated\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## Replace indexes\n\nIt is sometimes not preferable to throw an error if a unique constraint is violated. Instead, you may want to replace the existing object with the new one. This can be achieved by setting the `replace` property of the index to `true`.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nNow when we try to insert a user with an existing username, Isar will replace the existing user with the new one.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nReplace indexes also generate `putBy()` methods that allow you to update objects instead of replacing them. The existing id is reused, and links are still populated.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// user does not exist so this is the same as put()\nawait isar.users.putByUsername(user1);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nAs you can see, the id of the first inserted user is reused.\n\n## Case-insensitive indexes\n\nAll indexes on `String` and `List<String>` properties are case-sensitive by default. If you want to create a case-insensitive index, you can use the `caseSensitive` option:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## Index type\n\nThere are different types of indexes. Most of the time, you'll want to use an `IndexType.value` index, but hash indexes are more efficient.\n\n### Value index\n\nValue indexes are the default type and the only one allowed for all properties that don't hold Strings or Lists. Property values are used to build the index. In the case of lists, the elements of the list are used. It is the most flexible but also space-consuming of the three index types.\n\n:::tip\nUse `IndexType.value` for primitives, Strings where you need `startsWith()` where clauses, and Lists if you want to search for individual elements.\n:::\n\n### Hash index\n\nStrings and Lists can be hashed to reduce the storage required by the index significantly. The disadvantage of hash indexes is that they can't be used for prefix scans (`startsWith` where clauses).\n\n:::tip\nUse `IndexType.hash` for Strings and Lists if you don't need `startsWith`, and `elementEqualTo` where clauses.\n:::\n\n### HashElements index\n\nString lists can be hashed as a whole (using `IndexType.hash`), or the elements of the list can be hashed separately (using `IndexType.hashElements`), effectively creating a multi-entry index with hashed elements.\n\n:::tip\nUse `IndexType.hashElements` for `List<String>` where you need `elementEqualTo` where clauses.\n:::\n\n## Composite indexes\n\nA composite index is an index on multiple properties. Isar allows you to create composite indexes of up to three properties.\n\nComposite indexes are also known as multiple-column indexes.\n\nIt's probably best to start with an example. We create a person collection and define a composite index on the age and name properties:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**Data:**\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**Generated index:**\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nThe generated composite index contains all persons sorted by their age their name.\n\nComposite indexes are great if you want to create efficient queries sorted by multiple properties. They also enable advanced where clauses with multiple properties:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nThe last property of a composite index also supports conditions like `startsWith()` or `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Multi-entry indexes\n\nIf you index a list using `IndexType.value`, Isar will automatically create a multi-entry index, and each item in the list is indexed toward the object. It works for all types of lists.\n\nPractical applications for multi-entry indexes include indexing a list of tags or creating a full-text index.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` splits a string into words according to the [Unicode Annex #29](https://unicode.org/reports/tr29/) specification, so it works for almost all languages correctly.\n\n**Data:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nEntries with duplicate words only appear once in the index.\n\n**Generated index:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nThis index can now be used for prefix (or equality) where clauses of the individual words of the description.\n\n:::tip\nInstead of storing the words directly, also consider using the result of a [phonetic algorithm](https://en.wikipedia.org/wiki/Phonetic_algorithm) like [Soundex](https://en.wikipedia.org/wiki/Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/it/README.md",
    "content": "---\nhome: true\ntitle: Home\nheroImage: /isar.svg\nactions:\n  - text: Iniziamo!\n    link: /it/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Creato per Flutter\n    details: Setup minimo, facile da usare, nessuna configurazione, niente codice boilerplate. Basta aggiungere poche linee di codice per iniziare.\n  - title: 🚀 Altamente scalabile\n    details: Salva centinaia di migliaia di records in un singolo NoSQL database ed interrogalo in maniera efficiente ed asincrona.\n  - title: 🍭 Ricco di funzionalità\n    details: Isa ha un insieme ricco di funzionalità per aiutare nella gestione dei tuoi dati. Indici composti & multi-entry, modificatori di query, supporto al JSON, e molto altro. \n  - title: 🔎 Ricerca full-text\n    details: Isar ha un sistema di ricerca built-in basato su full-text. Crea un inidice multi-entry e ricerca i record facilmente. \n  - title: 🧪 Semantica ACID \n    details: Isar è conforme con ACID e gestisce automaticamente le transazioni. In caso di errore effettua roll-back automaticamente. \n  - title: 💃 Staticamente tipizzato\n    details: Le query Isar sono tipizzate staticamente e controllate in fase di compilazione. Non è necessario preoccuparsi degli errori di runtime.\n  - title: 📱 Multipiattaform\n    details: iOS, Android, Desktop e PIENO SUPPORTO AL WEB!\n  - title: ⏱ Asincrono\n    details: Operazioni di query parallele e supporto per isolamento multiplo pronto all'uso\n  - title: 🦄 Open Source\n    details: Tutto è open source e gratuito per sempre!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/it/crud.md",
    "content": "---\ntitle: Create, Read, Update, Delete\n---\n\n# Create, Read, Update, Delete\n\nQuando hai definito le tue collezioni, impara a manipolarle!\n\n## Apertura di Isar\n\nPrima che tu possa fare qualsiasi cosa, abbiamo bisogno di un'istanza Isar. Ogni istanza richiede una directory con autorizzazione di scrittura in cui è possibile archiviare il file di database. Se non specifichi una directory, Isar troverà una directory predefinita adatta per la piattaforma corrente.\n\nFornisci tutti gli schemi che desideri utilizzare con l'istanza Isar. Se apri più istanze, devi comunque fornire gli stessi schemi a ciascuna istanza.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [ContactSchema],\n  directory: dir.path,\n);\n```\n\nÈ possibile utilizzare la configurazione predefinita o fornire alcuni dei seguenti parametri:\n\n| Config. |  Descrizione |\n| -------| -------------|\n| `name` | Apri più istanze con nomi distinti. Per impostazione predefinita, viene utilizzato `\"predefinito\"`. |\n| `directory` | Il percorso di archiviazione per questa istanza. Puoi passare un percorso relativo o assoluto. Per impostazione predefinita, `NSDocumentDirectory` viene utilizzato per iOS e `getDataDirectory` per Android. Non richiesto per il web. |\n| `relaxedDurability` | Rilassa la garanzia di durata per aumentare le prestazioni di scrittura. In caso di arresto anomalo del sistema (non arresto anomalo dell'app), è possibile perdere l'ultima transazione impegnata. La corruzione non è possibile |\n| `compactOnLaunch` | Condizioni per verificare se il database deve essere compattato all'apertura dell'istanza. |\n| `inspector` | Abilita l'Inspector per le build di debug. Per le build di profili e versioni questa opzione viene ignorata. |\n\nSe un'istanza è già aperta, la chiamata a `Isar.open()` fornirà l'istanza esistente indipendentemente dai parametri specificati. È utile per usare Isar in un isolate.\n\n:::tip\nPrendi in considerazione l'utilizzo del pacchetto [path_provider](https://pub.dev/packages/path_provider) per ottenere un percorso valido su tutte le piattaforme.\n:::\n\nIl percorso di archiviazione del file di database è `directory/name.isar`.\n\n## Lettura dal database\n\nUsa le istanze di `IsarCollection` per trovare, interrogare e creare nuovi oggetti di un determinato tipo in Isar.\n\nPer gli esempi seguenti, assumiamo di avere una raccolta \"Ricetta\" definita come segue:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### Ottieni una raccolta\n\nTutte le tue raccolte vivono nell'istanza Isar. Puoi ottenere la raccolta di ricette con:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\nÈ stato facile! Se non vuoi usare le funzioni di accesso alla raccolta, puoi anche usare il metodo `collection()`:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### Ottieni un oggetto (per ID)\n\nNon abbiamo ancora dati nella raccolta, ma facciamo finta di farlo in modo da poter ottenere un oggetto immaginario con l'id `123`\n\n```dart\nfinal recipe = await recipes.get(123);\n```\n\n`get()` restituisce un `Future` con l'oggetto o `null` se non esiste. Tutte le operazioni Isar sono asincrone per impostazione predefinita e la maggior parte di esse ha una controparte sincrona:\n\n```dart\nfinal recipe = recipes.getSync(123);\n```\n\n:::warning\nPer impostazione predefinita, dovresti utilizzare la versione asincrona dei metodi nell'isolato dell'interfaccia utente. Poiché Isar è molto veloce, è spesso accettabile utilizzare la versione sincrona.\n:::\n\nSe vuoi ottenere più oggetti contemporaneamente, usa `getAll()` o `getAllSync()`:\n\n```dart\nfinal recipe = await recipes.getAll([1, 2]);\n```\n\n### Interroga gli oggetti\n\nInvece di ottenere oggetti per id puoi anche interrogare un elenco di oggetti che soddisfano determinate condizioni usando `.where()` e `.filter()`:\n\n```dart\nfinal allRecipes = await recipes.where().findAll();\n\nfinal favouires = await recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ Scopri di più: [Queries](queries)\n\n## Modifica del database\n\nÈ finalmente arrivato il momento di modificare la nostra collezione! Per creare, aggiornare o eliminare oggetti, utilizzare le rispettive operazioni racchiuse in una transazione di scrittura:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await recipes.get(123)\n\n  recipe.isFavorite = false;\n  await recipes.put(recipe); // perform update operations\n\n  await recipes.delete(123); // or delete operations\n});\n```\n\n➡️ Scopri di più: [Transactions](transactions)\n\n### Inserimento\n\nPer rendere persistente un oggetto in Isar, inserirlo in una collezione. Il metodo `put()` di Isar inserirà o aggiornerà l'oggetto a seconda che esista già nella raccolta.\n\nSe il campo id è `null` o `Isar.autoIncrement`, Isar utilizzerà un id di incremento automatico.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await recipes.put(pancakes);\n})\n```\n\nIsar assegnerà automaticamente l'id all'oggetto se il campo `id` non è definitivo.\n\nInserire più oggetti contemporaneamente è altrettanto facile:\n\n```dart\nawait isar.writeTxn(() async {\n  await recipes.putAll([pancakes, pizza]);\n})\n```\n\n### Aggiornamento\n\nSia la creazione che l'aggiornamento funzionano con `collection.put(object)`. Se l'id è `null` (o non esiste), l'oggetto viene inserito; in caso contrario, viene aggiornato.\n\nQuindi, se vogliamo eliminare i nostri pancake dai preferiti, possiamo fare quanto segue:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await recipes.put(recipe);\n});\n```\n\n### Eliminazione\n\nVuoi sbarazzarti di un oggetto in Isar? Usa `collection.delete(id)`. Il metodo delete restituisce se un oggetto con l'ID specificato è stato trovato ed eliminato. Se vuoi eliminare l'oggetto con id `123`, ad esempio, puoi fare:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nAllo stesso modo per get e put, esiste anche un'operazione di eliminazione in blocco che restituisce il numero di oggetti eliminati:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\nSe non conosci gli ID degli oggetti che desideri eliminare, puoi utilizzare una query:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/it/faq.md",
    "content": "---\ntitle: FAQ\n---\n\n# Domande frequenti\n\nUna raccolta casuale di domande frequenti sui database Isar e Flutter.\n\n### Perché ho bisogno di un database?\n\n> Conservo i miei dati in un database back-end, perché ho bisogno di Isar?.\n\nAncora oggi è molto comune non avere connessione dati se sei in metropolitana o in aereo o se vai a trovare tua nonna, che non ha WiFi e un segnale cellulare pessimo. Non dovresti lasciare che una cattiva connessione paralizzi la tua app!\n\n### Isar vs Hive\n\nLa risposta è semplice: Isar è stato [nato come sostituto di Hive](https://github.com/hivedb/hive/issues/246) e ora si trova in uno stato in cui consiglio di utilizzare sempre Isar invece di Hive.\n\n### Dove sono le clausole?!\n\n> Perché **_Io_** devo scegliere quale indice utilizzare?\n\nCi sono più ragioni. Molti database utilizzano l'euristica per scegliere l'indice migliore per una determinata query. Il database deve raccogliere dati di utilizzo aggiuntivi (-> sovraccarico) e potrebbe comunque scegliere l'indice errato. Inoltre, rende più lenta la creazione di una query.\n\nNessuno conosce i tuoi dati meglio di te, lo sviluppatore. Quindi puoi scegliere l'indice ottimale e decidere, ad esempio, se desideri utilizzare un indice per eseguire query o ordinare.\n\n### Devo usare gli indici oppure le clausole where?\n\nNo! Molto probabilmente Isar è abbastanza veloce se ti affidi solo ai filtri.\n\n### Isar è abbastanza veloce?\n\nIsar è tra i database più veloci per dispositivi mobili, quindi dovrebbe essere abbastanza veloce per la maggior parte dei casi d'uso. Se riscontri problemi di prestazioni, è probabile che tu stia facendo qualcosa di sbagliato.\n\n### Isar aumenta le dimensioni della mia app?\n\nUn po', sì. Isar aumenterà la dimensione del download della tua app di circa 1 - 1,5 MB. Isar Web aggiunge solo pochi KB.\n\n### La documentazione non è corretta / c'è un errore di battitura.\n\nOh no, mi dispiace. Per favore [apri un problema](https://github.com/isar-community/isar/issues/new/choose) o, ancora meglio, un PR per risolverlo 💪.\n"
  },
  {
    "path": "docs/docs/it/indexes.md",
    "content": "---\ntitle: Indici\n---\n\n# Indici\n\nGli indici sono la caratteristica più potente di Isar. Molti database incorporati offrono indici \"normali\" (se non del tutto), ma Isar ha anche indici compositi e multi-voce. Comprendere il funzionamento degli indici è essenziale per ottimizzare le prestazioni delle query. Isar ti permette di scegliere quale indice vuoi usare e come usarlo. Inizieremo con una rapida introduzione a cosa sono gli indici.\n\n## Cosa sono gli indici?\n\nQuando una raccolta non è indicizzata, è probabile che l'ordine delle righe non sia distinguibile dalla query in quanto non ottimizzato e la query dovrà quindi cercare tra gli oggetti in maniera lineare. In altre parole, la query dovrà cercare in ogni oggetto per trovare quelli che soddisfano le condizioni. Come puoi immaginare, può volerci del tempo. Guardare attraverso ogni singolo oggetto non è molto efficiente.\n\nAd esempio, questa raccolta `Product` non è ordinata.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**Dati:**\n\n| id  | nome      | prezzo |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nUna query che tenti di trovare tutti i prodotti che costano più di € 30 deve cercare in tutte e nove le righe. Questo non è un problema per nove righe, ma potrebbe diventare un problema per 100.000 righe.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nPer migliorare le prestazioni di questa query, indicizziamo la proprietà `price`. Un indice è come una tabella di ricerca ordinata:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**Indici generati:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nOra, la query può essere eseguita molto più velocemente. L'esecutore può saltare direttamente alle ultime tre righe dell'indice e trovare gli oggetti corrispondenti in base al loro ID.\n\n### Ordinamento\n\nUn'altra cosa interessante: gli indici possono eseguire un ordinamento super veloce. Le query ordinate sono costose perché il database deve caricare tutti i risultati in memoria prima di ordinarli. Anche se si specifica un offset o un limite, vengono applicati dopo l'ordinamento.\n\nImmaginiamo di voler trovare i quattro prodotti più economici. Potremmo usare la seguente query:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nIn questo esempio, il database dovrebbe caricare tutti (!) gli oggetti, ordinarli per prezzo e restituire i quattro prodotti con il prezzo più basso.\n\nCome probabilmente puoi immaginare, questo può essere fatto in modo molto più efficiente con l'indice precedente. Il database prende le prime quattro righe dell'indice e restituisce gli oggetti corrispondenti poiché sono già nell'ordine corretto.\n\nPer utilizzare l'indice per l'ordinamento, scriveremo la query in questo modo:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nLa clausola `.anyX()` indica a Isar di usare un indice solo per l'ordinamento. Puoi anche usare una clausola where come `.priceGreaterThan()` e ottenere risultati ordinati.\n\n## Indici univoci\n\nUn indice univoco garantisce che l'indice non contenga valori duplicati. Può essere costituito da una o più proprietà. Se un indice univoco ha una proprietà, i valori in questa proprietà saranno univoci. Se l'indice univoco ha più di una proprietà, la combinazione di valori in queste proprietà è univoca.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nQualsiasi tentativo di inserire o aggiornare i dati nell'indice univoco che causa un duplicato risulterà in un errore:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// try to insert user with same username\nawait isar.users.put(user2); // -> error: unique constraint violated\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## Sostituisci gli indici\n\nA volte non è preferibile generare un errore se viene violato un vincolo univoco. Invece, potresti voler sostituire l'oggetto esistente con quello nuovo. Questo può essere ottenuto impostando la proprietà `replace` dell'indice su `true`.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nOra quando proviamo a inserire un utente con un nome utente esistente, Isar sostituirà l'utente esistente con quello nuovo.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nSostituire gli indici genera anche metodi `putBy()` che ti consentono di aggiornare gli oggetti invece di sostituirli. L'ID esistente viene riutilizzato e i collegamenti continuano a essere popolati.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// user does not exist so this is the same as put()\nawait isar.users.putByUsername(user1); \nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nCome puoi vedere, l'id del primo utente inserito viene riutilizzato.\n\n## Indici senza distinzione tra maiuscole e minuscole\n\nTutti gli indici sulle proprietà `String` e `List<String>` fanno distinzione tra maiuscole e minuscole per impostazione predefinita. Se desideri creare un indice senza distinzione tra maiuscole e minuscole, puoi utilizzare l'opzione `caseSensitive`:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## Tipo di indice\n\nEsistono diversi tipi di indici. La maggior parte delle volte, vorrai usare un indice `IndexType.value`, ma gli indici hash sono più efficienti.\n\n### Indice di valore\n\nGli indici di valore sono il tipo predefinito e l'unico consentito per tutte le proprietà che non contengono stringhe o elenchi. I valori delle proprietà vengono utilizzati per creare l'indice. Nel caso di elenchi, vengono utilizzati gli elementi dell'elenco. È il più flessibile ma anche dispendioso in termini di spazio dei tre tipi di indice.\n\n:::tip\nUsa `IndexType.value` per le primitive, String dove hai bisogno della clausole-where `startsWith()` e List se vuoi cercare singoli elementi.\n:::\n\n### Indice hash\n\nÈ possibile eseguire l'hashing di stringhe ed elenchi per ridurre significativamente lo spazio di archiviazione richiesto dall'indice. Lo svantaggio degli indici hash è che non possono essere usati per scansioni di prefissi (clausole-where `startsWith`).\n\n:::tip\nUsa `IndexType.hash` per stringhe ed elenchi se non hai bisogno di clausole-where `startsWith` e `elementEqualTo`.\n:::\n\n### Indice HashElements\n\nGli elenchi di stringhe possono essere sottoposti a hash per intero (usando `IndexType.hash`), oppure gli elementi dell'elenco possono essere sottoposti a hash separatamente (usando `IndexType.hashElements`), creando in modo efficace un indice multi-voce con elementi hash.\n\n:::tip\nUsa `IndexType.hashElements` per `List<String>` dove hai bisogno di clausole-where `elementEqualTo`.\n:::\n\n## Indici compositi\n\nUn indice composito è un indice su più proprietà. Isar consente di creare indici compositi fino a tre proprietà.\n\nGli indici compositi sono anche noti come indici a più colonne.\n\nProbabilmente è meglio iniziare con un esempio. Creiamo una collezione di persone e definiamo un indice composito sulle proprietà di età e nome:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**Dati:**\n\n| id  | nome   | età | città natale  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**Indici generati:**\n\n| età | nome   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nL'indice composito generato contiene tutte le persone ordinate per età e nome.\n\nGli indici compositi sono ottimi se desideri creare query efficienti ordinate in base a più proprietà. Consentono anche clausole dove avanzate con più proprietà:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nL'ultima proprietà di un indice composito supporta anche condizioni come `startsWith()` o `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Indici a più voci\n\nSe indicizzi una lista usando 'IndexType.value', Isar creerà automaticamente un indice multi-voce e ogni voce nella lista viene indicizzata verso l'oggetto. Funziona per tutti i tipi di liste.\n\nLe applicazioni pratiche per gli indici a voci multiple includono l'indicizzazione di un elenco di tag o la creazione di un indice full-text.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` divide una stringa in parole secondo la specifica [Unicode Annex #29](https://unicode.org/reports/tr29/), quindi funziona correttamente per quasi tutte le lingue.\n\n**Dati:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nLe voci con parole duplicate vengono visualizzate solo una volta nell'indice.\n\n**Indici generati:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nQuesto indice può ora essere usato per prefisso (o uguaglianza) dove clausole delle singole parole della descrizione.\n\n:::tip\nInvece di memorizzare direttamente le parole, considera anche l'utilizzo del risultato di un [algoritmo fonetico](https://en.wikipedia.org/wiki/Algoritmo_fonetico) come [Soundex](https://en.wikipedia.org/wiki/ Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/it/limitations.md",
    "content": "# Limitazioni\n\nCome sapete, Isar funziona su dispositivi mobili e desktop in esecuzione su VM oltre che su Web. Entrambe le piattaforme sono molto diverse e hanno limitazioni diverse.\n\n## Limitazioni VM\n\n- Solo i primi 1024 byte di una stringa possono essere usati per un prefisso clausola-where\n- Gli oggetti possono avere una dimensione di soli 16 MB\n\n## Limitazioni Web\n\nPoiché Isar Web si basa su IndexedDB, ci sono più limitazioni ma sono appena percettibili durante l'utilizzo di Isar.\n\n- I metodi sincroni non sono supportati\n- Attualmente, i filtri `Isar.splitWords()` e `.matches()` non sono ancora implementati\n- Le modifiche allo schema non vengono controllate rigorosamente come nella VM, quindi fai attenzione a rispettare le regole\n- Tutti i tipi di numeri sono memorizzati come double (l'unico tipo di numero js) quindi `@Size32` non ha alcun effetto\n- Gli indici sono rappresentati in modo diverso, quindi gli indici hash non utilizzano meno spazio (funzionano comunque allo stesso modo)\n- `col.delete()` e `col.deleteAll()` funzionano correttamente ma il valore restituito non è corretto\n- `col.clear()` non reimposta il valore di incremento automatico\n- `NaN` non è supportato come valore\n"
  },
  {
    "path": "docs/docs/it/links.md",
    "content": "---\ntitle: Collegamenti\n---\n\n# Collegamenti\n\nI collegamenti consentono di esprimere relazioni tra oggetti, come l'autore di un commento (Utente). Puoi modellare le relazioni `1:1`, `1:n` e `n:n` con i collegamenti Isar. L'uso dei collegamenti è meno ergonomico rispetto all'utilizzo di oggetti incorporati e dovresti utilizzare oggetti incorporati quando possibile.\n\nPensa al collegamento come a una tabella separata che contiene la relazione. È simile alle relazioni SQL ma ha un set di funzionalità e un'API diversi.\n\n## IsarLink\n\n`IsarLink<T>` può contenere nessuno o un oggetto correlato e può essere utilizzato per esprimere una relazione a uno. `IsarLink` ha una singola proprietà chiamata `value` che contiene l'oggetto collegato.\n\nI collegamenti sono pigri, quindi è necessario dire a `IsarLink` di caricare o salvare il `valore` in modo esplicito. Puoi farlo chiamando `linkProperty.load()` e `linkProperty.save()`.\n\n:::tip\nLa proprietà id delle raccolte di origine e di destinazione di un collegamento deve essere non definitiva.\n:::\n\nPer i target non web, i link vengono caricati automaticamente quando li usi per la prima volta. Iniziamo aggiungendo un IsarLink a una collezione:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nAbbiamo definito un legame tra insegnanti e studenti. Ogni studente può avere esattamente un insegnante in questo esempio.\n\nInnanzitutto, creiamo l'insegnante e lo assegniamo a uno studente. Dobbiamo effettuare un `.put()` per l'insegnante e salvare il collegamento manualmente.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\nOra possiamo usare il link:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nProviamo la stessa cosa con il codice sincrono. Non è necessario salvare il collegamento manualmente perché `.putSync()` salva automaticamente tutti i collegamenti. Crea anche l'insegnante per noi.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nAvrebbe più senso se lo studente dell'esempio precedente potesse avere più insegnanti. Fortunatamente, Isar ha `IsarLinks<T>`, che può contenere più oggetti correlati ed esprimere una relazione a molti.\n\n`IsarLinks<T>` estende `Set<T>` ed espone tutti i metodi consentiti per gli insiemi.\n\n`IsarLinks` si comporta in modo molto simile a `IsarLink` ed è anche pigro. Per caricare tutti gli oggetti collegati chiama `linkProperty.load()`. Per rendere persistenti le modifiche, chiama `linkProperty.save()`.\n\nInternamente sia \"IsarLink\" che \"IsarLinks\" sono rappresentati allo stesso modo. Possiamo aggiornare `IsarLink<Teacher>` da prima a un `IsarLinks<Teacher>` per assegnare più insegnanti a un singolo studente (senza perdere dati).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nFunziona perché non abbiamo cambiato il nome del collegamento (`teacher`), quindi Isar lo ricorda da prima.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Backlink\n\nVi sento chiedere: \"E se volessimo esprimere relazioni inverse?\". Non preoccuparti; ora introdurremo i backlink.\n\nI backlink sono collegamenti nella direzione inversa. Ogni link ha sempre un backlink implicito. Puoi renderlo disponibile per la tua app annotando un `IsarLink` o `IsarLinks` con `@Backlink()`.\n\nI backlink non richiedono memoria o risorse aggiuntive; puoi aggiungerli, rimuoverli e rinominarli liberamente senza perdere dati.\n\nVogliamo sapere quali studenti ha un insegnante specifico, quindi definiamo un backlink:\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nDobbiamo specificare il collegamento a cui punta il backlink. È possibile avere più collegamenti diversi tra due oggetti.\n\n## Inizializza i collegamenti\n\n`IsarLink` e `IsarLinks` hanno un costruttore arg zero, che dovrebbe essere usato per assegnare la proprietà link quando l'oggetto viene creato. È buona norma rendere le proprietà del collegamento \"finali\".\n\nQuando `metti()` il tuo oggetto per la prima volta, il collegamento viene inizializzato con la raccolta di origine e destinazione e puoi chiamare metodi come `load()` e `save()`. Un collegamento inizia a tenere traccia delle modifiche subito dopo la sua creazione, quindi puoi aggiungere e rimuovere relazioni anche prima che il collegamento venga inizializzato.\n\n:::danger\nÈ vietato spostare un collegamento a un altro oggetto.\n:::\n"
  },
  {
    "path": "docs/docs/it/queries.md",
    "content": "---\ntitle: Query\n---\n\n# Query\n\nLa query è il modo in cui trovi i record che soddisfano determinate condizioni, ad esempio:\n\n- Trova tutti i contatti speciali\n- Trova nomi distinti nei contatti\n- Elimina tutti i contatti che non hanno il cognome definito\n\nPoiché le query vengono eseguite sul database e non in Dart, sono molto veloci. Quando usi in modo intelligente gli indici, puoi migliorare ulteriormente le prestazioni delle query. Di seguito, imparerai come scrivere query e come renderle il più velocemente possibile.\n\nEsistono due diversi metodi per filtrare i record: i filtri e le clausole where. Inizieremo dando un'occhiata a come funzionano i filtri.\n\n## Filtri\n\nI filtri sono facili da usare e da capire. A seconda del tipo di proprietà, sono disponibili diverse operazioni di filtro, la maggior parte delle quali ha nomi autoesplicativi.\n\nI filtri funzionano valutando un'espressione per ogni oggetto della raccolta che viene filtrata. Se l'espressione si risolve in `true`, Isar include l'oggetto nei risultati. I filtri non influiscono sull'ordine dei risultati.\n\nUtilizzeremo il seguente modello per gli esempi seguenti:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### Condizioni di query\n\nA seconda del tipo di campo, sono disponibili diverse condizioni.\n\n| Condizione | Descrizione |\n| ----------| ------------|\n| `.equalTo(value)` | Corrisponde a valori uguali al `value` specificato. |\n| `.between(lower, upper)` | Corrisponde ai valori compresi tra `lower` and `upper`. |\n| `.greaterThan(bound)` | Corrisponde a valori maggiori di `bound`. |\n| `.lessThan(bound)` | Corrisponde a valori inferiori a `bound`. I valori `null` verranno inclusi per impostazione predefinita perché `null` è considerato inferiore a qualsiasi altro valore. |\n| `.isNull()` | Corrisponde a valori `null'.|\n| `.isNotNull()` | Corrisponde a valori che non sono `null'.|\n| `.length()` | Le query su List, String e lunghezza del collegamento filtrano gli oggetti in base al numero di elementi in un elenco o in un collegamento. |\n\nSupponiamo che il database contenga quattro scarpe con le taglie 39, 40, 46 e una con una taglia non impostata (`null`). A meno che non si esegua l'ordinamento, i valori verranno restituiti ordinati per id.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### Operatori logici\n\nÈ possibile comporre predicati utilizzando i seguenti operatori logici:\n\n| Operatore   | Descrizione |\n| ---------- | ----------- |\n| `.and()`   | Valuta come `true` se entrambe le espressioni della lato sinistro e della lato destro restituiscono `true`. |\n| `.or()`    | Valuta come `true` se una delle espressioni restituisce `true`. |\n| `.xor()`   | Valuta come `true` se esattamente un'espressione restituisce `true`. |\n| `.not()`   | Nega il risultato della seguente espressione. |\n| `.group()` | Raggruppa le condizioni e consente di specificare l'ordine di valutazione. |\n\nSe vuoi trovare tutte le scarpe nella taglia 46, puoi utilizzare la seguente query:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nSe vuoi usare più di una condizione, puoi combinare più filtri usando **and** logico `.and()`, **or** logico `.or()` e **xor** logico `. xor()`.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Optional. Filters are implicitly combined with logical and.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nQuesta query equivale a: `size == 46 && isUnisex == true`.\n\nPuoi anche raggruppare le condizioni usando `.group()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nQuesta query equivale a `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nPer negare una condizione o un gruppo, usa la logica **not** `.not()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nQuesta query equivale a `size != 46 && isUnisex != true`.\n\n### Condizioni di stringa\n\nOltre alle condizioni di query precedenti, i valori String offrono alcune condizioni in più che puoi utilizzare. I caratteri jolly simili a Regex, ad esempio, consentono una maggiore flessibilità nella ricerca.\n\n| Condizione            | Descrizione                                                       |\n| -------------------- | ----------------------------------------------------------------- |\n| `.startsWith(value)` | Corrisponde ai valori di stringa che iniziano con il `valore` fornito.          |\n| `.contains(value)`   | Corrisponde ai valori di stringa che contengono il `valore` fornito.          |\n| `.endsWith(value)`   | Corrisponde ai valori di stringa che terminano con il `valore` fornito.         |\n| `.matches(wildcard)` | Corrisponde ai valori di stringa che corrispondono al modello `jolly` fornito. |\n\n**Maiuscole/minuscole**\nTutte le operazioni sulle stringhe hanno un parametro `caseSensitive` opzionale che per impostazione predefinita è `true`.\n\n**Wildcards:**  \n**Caratteri jolly:**\nUna [espressione di stringa con caratteri jolly](https://en.wikipedia.org/wiki/Wildcard_character) è una stringa che utilizza caratteri normali con due caratteri jolly speciali:\n\n- Il carattere jolly `*` corrisponde a zero o più caratteri\n- Il carattere jolly `?` corrisponde a qualsiasi carattere.\n   Ad esempio, la stringa di caratteri jolly `\"d?g\"` corrisponde a `\"dog\"`, `\"dig\"` e `\"dug\"`, ma non a `\"ding\"`, `\"dg\"` o `\" un cane\"`.\n\n### Modificatori di query\n\nA volte è necessario creare una query in base ad alcune condizioni o per valori diversi. Isar ha uno strumento molto potente per la creazione di query condizionali:\n\n| Modificatore              | Descrizione                                          |\n| --------------------- | ---------------------------------------------------- |\n| `.optional(cond, qb)` | Estende la query solo se la `condition` è `true`. Questo può essere utilizzato quasi ovunque in una query, ad esempio per ordinarlo o limitarlo in modo condizionale. |\n| `.anyOf(list, qb)`    | Estende la query per ogni valore in `values` e combina le condizioni utilizzando la logica **or**. |\n| `.allOf(list, qb)`    | Estende la query per ogni valore in `values` e combina le condizioni utilizzando **and** logici. |\n\nIn questo esempio, costruiamo un metodo in grado di trovare scarpe con un filtro opzionale:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // only apply filter if sizeFilter != null\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nSe vuoi trovare tutte le scarpe che hanno una di più misure di scarpe, puoi scrivere una query convenzionale o utilizzare il modificatore `anyOf()`:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nI modificatori di query sono particolarmente utili quando si desidera creare query dinamiche.\n\n### Liste\n\nSi possono interrogare anche le liste:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nÈ possibile eseguire query in base alla lunghezza della lista:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nQuesti sono equivalenti al codice Dart `tweets.where((t) => t.hashtags.isEmpty);` e `tweets.where((t) => t.hashtags.length > 5);`. Puoi anche interrogare in base agli elementi dell'elenco:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nQuesto equivale al codice Dart `tweets.where((t) => t.hashtags.contains('flutter'));`.\n\n### Oggetti incorporati\n\nGli oggetti incorporati sono una delle funzionalità più utili di Isar. Possono essere interrogati in modo molto efficiente utilizzando le stesse condizioni disponibili per gli oggetti di livello superiore. Supponiamo di avere il seguente modello:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nVogliamo interrogare tutte le auto che hanno un marchio con il nome `\"BMW\"` e il paese `\"Germania\"`. Possiamo farlo usando la seguente query:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nCerca sempre di raggruppare le query nidificate. La query precedente è più efficiente della seguente. Anche se il risultato è lo stesso:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### Collegamenti\n\nSe il tuo modello contiene [link o backlink](links) puoi filtrare la tua query in base agli oggetti collegati o al numero di oggetti collegati.\n\n:::warning\nTieni presente che le query di collegamento possono essere costose perché Isar ha bisogno di cercare oggetti collegati. Considera invece l'utilizzo di oggetti incorporati.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nVogliamo trovare tutti gli studenti che hanno un insegnante di matematica o inglese:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nI filtri di collegamento restituiscono `true` se almeno un oggetto collegato soddisfa le condizioni.\n\nCerchiamo tutti gli studenti che non hanno insegnanti:\n  \n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\no in alternativa:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Clausole Where\n\nLe clausole where sono uno strumento molto potente, ma può essere un po' difficile metterle in pratica.\n\nA differenza dei filtri le clausole where utilizzano gli indici definiti nello schema per verificare le condizioni della query. Interrogare un indice è molto più veloce che filtrare ogni record individualmente.\n\n➡️ Scopri di più: [Indici](indexes)\n\n:::tip\nCome regola di base, dovresti sempre cercare di ridurre il più possibile i record usando le clausole where e fare il filtraggio rimanente usando i filtri.\n:::\n\nPuoi combinare solo le clausole where usando **or** logici. In altre parole, puoi sommare più clausole where insieme, ma non puoi interrogare l'intersezione di più clausole where.\n\nAggiungiamo gli indici alla collezione di scarpe:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nCi sono due indici. L'indice su `size` ci permette di usare clausole where come `.sizeEqualTo()`. L'indice composito su `isUnisex` consente dove clausole come `isUnisexSizeEqualTo()`. Ma anche `isUnisexEqualTo()` perché puoi sempre usare qualsiasi prefisso di un indice.\n\nOra possiamo riscrivere la query precedente che trova scarpe unisex della taglia 46 utilizzando l'indice composito. Questa query sarà molto più veloce della precedente:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nLe clausole where hanno altri due superpoteri: ti danno l'ordinamento \"gratuito\" e un'operazione distinta super veloce.\n\n### Combinare clausole where e filtri\n\nRicordi le query `shoes.filter()`? In realtà è solo una scorciatoia per `shoes.where().filter()`. Puoi (e dovresti) combinare dove clausole e filtri nella stessa query per utilizzare i vantaggi di entrambi:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nLa clausola where viene applicata per prima per ridurre il numero di oggetti da filtrare. Quindi il filtro viene applicato agli oggetti rimanenti.\n\n## Ordinamento\n\nÈ possibile definire come ordinare i risultati durante l'esecuzione della query utilizzando i metodi `.sortBy()`, `.sortByDesc()`, `.thenBy()` e `.thenByDesc()`.\n\nPer trovare tutte le scarpe ordinate per nome del modello in ordine crescente e taglia in ordine decrescente senza utilizzare un indice:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nOrdinare molti risultati può essere costoso, soprattutto perché l'ordinamento avviene prima dell'offset e del limit. I metodi di ordinamento sopra non fanno mai uso di indici. Fortunatamente, possiamo di nuovo utilizzare l'ordinamento della clausola where e rendere la nostra query fulminea anche se dobbiamo ordinare un milione di oggetti.\n\n### Ordinamento delle clausole where\n\nSe utilizzi una clausola **singola** nella query, i risultati sono già ordinati in base all'indice. Questo è un grosso problema!\n\nSupponiamo di avere scarpe nelle taglie `[43, 39, 48, 40, 42, 45]` e di voler trovare tutte le scarpe con una taglia maggiore di `42` e anche ordinarle per taglia:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // also sorts the results by size\n  .findAll(); // -> [43, 45, 48]\n```\n\nCome puoi vedere, il risultato è ordinato in base all'indice `size`. Se vuoi invertire l'ordinamento della clausola where, puoi impostare `sort` su `Sort.desc`:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nA volte non si desidera utilizzare una clausola where ma comunque beneficiare dell'ordinamento implicito. Puoi usare la clausola `any` where:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nSe utilizzi un indice composto, i risultati vengono ordinati in base a tutti i campi dell'indice.\n\n:::tip\nSe hai bisogno che i risultati siano ordinati, considera l'utilizzo di un indice a tale scopo. Soprattutto se lavori con `offset()` e `limit()`.\n:::\n\nA volte non è possibile o utile utilizzare un indice per l'ordinamento. In questi casi, dovresti utilizzare gli indici per ridurre il più possibile il numero di voci risultanti.\n\n## Valori univoci\n\nPer restituire solo voci con valori univoci, utilizzare il predicato distinto. Ad esempio, per scoprire quanti diversi modelli di scarpe hai nel tuo database Isar:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nPuoi anche concatenare più condizioni distinte per trovare tutte le scarpe con combinazioni di taglia modello distinte:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nViene restituito solo il primo risultato di ogni combinazione distinta. È possibile utilizzare le clausole where e le operazioni di ordinamento per controllarlo.\n\n### Clausola where distinta\n\nSe hai un indice non univoco, potresti voler ottenere tutti i suoi valori distinti. Potresti usare l'operazione `distinctBy` della sezione precedente, ma viene eseguita dopo l'ordinamento e i filtri, quindi c'è un po' di sovraccarico.\nSe utilizzi solo una singola clausola where, puoi invece fare affidamento sull'indice per eseguire l'operazione distinta.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nIn teoria, potresti anche usare più clausole where per l'ordinamento e la distinzione. L'unica restrizione è per quelle clausole where che non si sovrappongono e utilizzano lo stesso indice. Per un corretto ordinamento, devono anche essere applicati in ordine di ordinamento. Stai molto attento se fai affidamento su questo!\n:::\n\n## Offset e limit\n\nSpesso è una buona idea limitare il numero di risultati di una query per le visualizzazioni lazy di liste. Puoi farlo impostando un `limit()`:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nImpostando un `offset()` puoi anche impaginare i risultati della tua query.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nPoiché la creazione di un'istanza di oggetti Dart è spesso la parte più costosa dell'esecuzione di una query, è una buona idea caricare solo gli oggetti necessari.\n\n## Ordine di esecuzione\n\nIsar esegue le query sempre nello stesso ordine:\n\n1. Attraversa l'indice primario o secondario per trovare gli oggetti (applica le clausole where)\n2. Filtra gli oggetti\n3. Ordina i risultati\n4. Applicare un'operazione distinta\n5. Risultato offset e limite\n6. Restituisci i risultati\n\n## Operazioni di query\n\nNegli esempi precedenti, abbiamo usato `.findAll()` per recuperare tutti gli oggetti corrispondenti. Ci sono più operazioni disponibili, tuttavia:\n\n| Operazione        | Descrizione                                                                                                         |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | Recupera solo il primo oggetto corrispondente o `null` se nessuno corrisponde.                                                  |\n| `.findAll()`     | Recupera tutti gli oggetti corrispondenti.                                                                                      |\n| `.count()`       | Conta quanti oggetti corrispondono alla query.                                                                             |\n| `.deleteFirst()` | Elimina il primo oggetto corrispondente dalla raccolta.                                                               |\n| `.deleteAll()`   | Elimina tutti gli oggetti corrispondenti dalla raccolta.                                                                    |\n| `.build()`       | Compila la query per riutilizzarla in seguito. Ciò consente di risparmiare il costo per creare una query se si desidera eseguirla più volte. |\n\n## Query sulla proprietà\n\nSe sei interessato solo ai valori di una singola proprietà, puoi utilizzare una query di proprietà. Basta creare una query normale e selezionare una proprietà:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nL'utilizzo di una sola proprietà consente di risparmiare tempo durante la deserializzazione. Le query sulle proprietà funzionano anche per gli oggetti e gli elenchi incorporati.\n\n## Aggregazione\n\nIsar supporta l'aggregazione dei valori di una query di proprietà. Sono disponibili le seguenti operazioni di aggregazione:\n\n| Operazione    | Descrizione                                                    |\n| ------------ | -------------------------------------------------------------- |\n| `.min()`     | Trova il valore minimo o `null` se nessuno corrisponde.             |\n| `.max()`     | Trova il valore massimo o `null` se nessuno corrisponde.             |\n| `.sum()`     | Somma tutti i valori.                                               |\n| `.average()` | Calcola la media di tutti i valori o 'NaN' se nessuno corrisponde. |\n\nL'utilizzo delle aggregazioni è molto più veloce rispetto alla ricerca di tutti gli oggetti corrispondenti e all'esecuzione manuale dell'aggregazione.\n\n## Query dinamiche\n\n:::danger\nQuesta sezione molto probabilmente non è rilevante per te. È sconsigliato utilizzare query dinamiche a meno che non sia assolutamente necessario (e raramente lo fai).\n:::\n\nTutti gli esempi precedenti hanno utilizzato QueryBuilder e i metodi di estensione statica generati. Forse vuoi creare query dinamiche o un linguaggio di query personalizzato (come Isar Inspector). In tal caso, puoi usare il metodo `buildQuery()`:\n\n| Parametro       | Descrizione                                                                                 |\n| --------------- | ------------------------------------------------------------------------------------------- |\n| `whereClauses`  | Le clausole where della query.                                                             |\n| `whereDistinct` | Se le clausole where devono restituire valori distinti (utile solo per clausole where singole). |\n| `whereSort`     | L'ordine di scorrimento delle clausole where (utile solo per le clausole where singole).             |\n| `filter`        | Il filtro da applicare ai risultati.                                                        |\n| `sortBy`        | Un elenco di proprietà da ordinare.                                                            |\n| `distinctBy`    | Un elenco di proprietà da distinguere.                                                        |\n| `offset`        | L'offset dei risultati.                                                                  |\n| `limit`         | Il numero massimo di risultati da restituire.                                                    |\n| `property`      | Se non-null, vengono restituiti solo i valori di questa proprietà.                                 |\n\nCreiamo una query dinamica:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nLa seguente query è equivalente:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/it/recipes/data_migration.md",
    "content": "---\ntitle: Migrazione dei dati\n---\n\n# Migrazione dei dati\n\nIsar migra automaticamente gli schemi del database se aggiungi o rimuovi raccolte, campi o indici. A volte potresti voler migrare anche i tuoi dati. Isar non offre una soluzione integrata perché imporrebbe restrizioni alle migrazioni arbitrarie. È facile implementare la logica di migrazione adatta alle tue esigenze.\n\nVogliamo utilizzare una singola versione per l'intero database in questo esempio. Utilizziamo le preferenze condivise per archiviare la versione corrente e confrontarla con la versione a cui vogliamo migrare. Se le versioni non corrispondono, migriamo i dati e aggiorniamo la versione.\n\n:::tip\nPuoi anche assegnare a ciascuna raccolta la propria versione e migrarle individualmente.\n:::\n\nImmagina di avere una raccolta di utenti con un campo di compleanno. Nella versione 2 della nostra app, abbiamo bisogno di un campo aggiuntivo per l'anno di nascita per interrogare gli utenti in base all'età.\n\nVersione 1:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersione 2:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\nIl problema è che i modelli utente esistenti avranno un campo `birthYear` vuoto perché non esisteva nella versione 1. Abbiamo bisogno di migrare i dati per impostare il campo `birthYear`.\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // If the version is not set (new installation) or already 2, we do not need to migrate\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // Update version\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // We paginate through the users to avoid loading all users into memory at once\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // We don't need to update anything since the birthYear getter is used\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\nSe devi migrare molti dati, prendi in considerazione l'utilizzo di un isolamento in background per evitare sollecitazioni sul thread dell'interfaccia utente.\n:::\n"
  },
  {
    "path": "docs/docs/it/recipes/full_text_search.md",
    "content": "---\ntitle: Ricerca full-text\n---\n\n# Ricerca full-text\n\nLa ricerca full-text è un modo efficace per cercare il testo nel database. Dovresti già avere familiarità con il funzionamento degli [indici](../indexes.md), ma andiamo oltre le basi.\n\nUn indice funziona come una tabella di ricerca, consentendo al motore di query di trovare rapidamente i record con un determinato valore. Ad esempio, se hai un campo `title` nel tuo oggetto, puoi creare un indice su quel campo per rendere più veloce la ricerca di oggetti con un determinato titolo.\n\n## Perché la ricerca full-text è utile?\n\nPuoi cercare facilmente il testo usando i filtri. Esistono varie operazioni sulle stringhe, ad esempio `.startsWith()`, `.contains()` e `.matches()`. Il problema con i filtri è che il loro runtime è `O(n)` dove `n` è il numero di record nella raccolta. Le operazioni sulle stringhe come `.matches()` sono particolarmente costose.\n\n:::tip\nLa ricerca full-text è molto più veloce dei filtri, ma gli indici presentano alcune limitazioni. In questa ricetta, esploreremo come aggirare queste limitazioni.\n:::\n\n## Esempio di base\n\nL'idea è sempre la stessa: invece di indicizzare l'intero testo, indicizziamo le parole nel testo in modo da poterle cercare singolarmente.\n\nCreiamo l'indice full-text più semplice:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nOra possiamo cercare messaggi con parole specifiche nel contenuto:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nQuesta query è super veloce, ma ci sono alcuni problemi:\n\n1. Possiamo cercare solo parole intere\n2. Non consideriamo la punteggiatura\n3. Non supportiamo altri caratteri di spazio vuoto\n\n## Dividere il testo nel modo giusto\n\nProviamo a migliorare l'esempio precedente. Potremmo provare a sviluppare un'espressione regolare complicata per correggere la divisione delle parole, ma probabilmente sarà lenta e sbagliata per i casi limite.\n\nL'[Unicode Annex #29](https://unicode.org/reports/tr29/) definisce come dividere correttamente il testo in parole per quasi tutte le lingue. È piuttosto complicato, ma fortunatamente Isar fa il lavoro pesante per noi:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## Voglio più controllo\n\nFacilissimo! Possiamo modificare il nostro indice anche per supportare la corrispondenza dei prefissi e la corrispondenza senza distinzione tra maiuscole e minuscole:\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nPer impostazione predefinita, Isar memorizzerà le parole come valori hash che sono veloci ed efficienti in termini di spazio. Ma gli hash non possono essere usati per la corrispondenza dei prefissi. Usando `IndexType.value`, possiamo cambiare l'indice per usare invece le parole direttamente. Ci fornisce la clausola where `.titleWordsAnyStartsWith()`:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## Ho anche bisogno di `.endsWith()`\n\nSicuramente! Useremo un trucco per ottenere la corrispondenza `.endsWith()`:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nNon dimenticare di invertire il finale che vuoi cercare:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Algoritmi di derivazione\n\nSfortunatamente, gli indici non supportano la corrispondenza `.contains()` (questo vale anche per altri database). Ma ci sono alcune alternative che vale la pena esplorare. La scelta dipende molto dal tuo utilizzo. Un esempio è l'indicizzazione delle radici delle parole anziché dell'intera parola.\n\nUn algoritmo stemming è un processo di normalizzazione linguistica in cui le forme varianti di una parola sono ridotte a una forma comune:\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\nGli algoritmi più diffusi sono [Algoritmo di stemming di Porter](https://tartarus.org/martin/PorterStemmer/) e [Algoritmi di stemming di Snowball](https://snowballstem.org/algorithms/).\n\nEsistono anche forme più avanzate come [lemmatizzazione](https://en.wikipedia.org/wiki/Lemmatizzazione).\n\n## Algoritmi fonetici\n\nUn [algoritmo fonetico](https://en.wikipedia.org/wiki/Phonetic_algorithm) è un algoritmo per indicizzare le parole in base alla loro pronuncia. In altre parole, ti permette di trovare parole che suonano simili a quelle che stai cercando.\n\n:::warning\nLa maggior parte degli algoritmi fonetici supporta solo una singola lingua.\n:::\n\n### Soundex\n\n[Soundex](https://en.wikipedia.org/wiki/Soundex) è un algoritmo fonetico per indicizzare i nomi in base al suono, come si pronuncia in inglese. L'obiettivo è che gli omofoni siano codificati nella stessa rappresentazione in modo che possano essere abbinati nonostante piccole differenze nell'ortografia. È un algoritmo semplice e ci sono più versioni migliorate.\n\nUsando questo algoritmo, sia `\"Robert\"` che `\"Rupert\"` restituiscono la stringa `\"R163\"` mentre `\"Rubin\"` restituisce `\"R150\"`. `\"Ashcraft\"` e `\"Ashcroft\"` producono entrambi `\"A261\"`.\n\n### Double Metaphone\n\nL'algoritmo di codifica fonetica [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) è la seconda generazione di questo algoritmo. Apporta diversi miglioramenti di progettazione fondamentali rispetto all'algoritmo Metaphone originale.\n\nDouble Metaphone spiega varie irregolarità in inglese di origine slava, germanica, celtica, greca, francese, italiana, spagnola, cinese e di altro tipo.\n"
  },
  {
    "path": "docs/docs/it/recipes/multi_isolate.md",
    "content": "---\ntitle: Utilizzo multi-isolate\n---\n\n# Utilizzo multi-isolate\n\nInvece dei thread, tutto il codice Dart viene eseguito all'interno degli isolate. Ogni isolate ha il proprio heap di memoria, assicurando che nessuno degli stati in un isolate sia accessibile da qualsiasi altro isolate.\n\nÈ possibile accedere a Isar da più isolate contemporaneamente e anche gli osservatori lavorano tra gli isolate. In questa ricetta vedremo come utilizzare Isar in un ambiente multiisolate.\n\n## Quando utilizzare più isolate\n\nLe transazioni Isar vengono eseguite in parallelo anche se eseguite nello stesso isolate. In alcuni casi, è comunque vantaggioso accedere all'Isar da più isolate.\n\nIl motivo è che Isar impiega parecchio tempo a codificare e decodificare i dati da e verso gli oggetti Dart. Puoi pensarlo come codifica e decodifica JSON (solo più efficiente). Queste operazioni vengono eseguite all'interno dell'isolate da cui si accede ai dati e bloccano naturalmente altro codice nell'isolate. In altre parole: Isar esegue parte del lavoro nel tuo isolate Dart.\n\nSe hai solo bisogno di leggere o scrivere poche centinaia di oggetti contemporaneamente, farlo nell'isolate dell'interfaccia utente non è un problema. Ma per transazioni enormi o se il thread dell'interfaccia utente è già occupato, dovresti considerare l'utilizzo di un isolate separato.\n\n## Esempio\n\nLa prima cosa che dobbiamo fare è aprire l'Isar nel nuovo isolate. Poiché l'istanza di Isar è già aperta nell'isolate principale, `Isar.open()` restituirà la stessa istanza.\n\n:::warning\nAssicurati di fornire gli stessi schemi dell'isolate principale. In caso contrario, riceverai un errore.\n:::\n\n`compute()` avvia un nuovo isolate in Flutter ed esegue la funzione data in esso.\n\n```dart\nvoid main() {\n  // Open Isar in the UI isolate\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // listen to changes in the database\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // start a new isolate and create 10000 messages\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // after some time:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// function that will be executed in the new isolate\nFuture createDummyMessages(int count) async {\n  // we don't need the path here because the instance is already open\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // we use a synchronous transactions in isolates\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nCi sono alcune cose interessanti da notare nell'esempio sopra:\n\n- `isar.messages.watchLazy()` viene chiamato nell'isolate dell'interfaccia utente e viene notificato delle modifiche da un altro isolate.\n- Le istanze sono referenziate per nome. Il nome predefinito è `default`, ma in questo esempio lo impostiamo su `myInstance`.\n- Abbiamo utilizzato una transazione sincrona per creare i messaggi. Bloccare il nostro nuovo isolate non è un problema e le transazioni sincrone sono un po' più veloci.\n"
  },
  {
    "path": "docs/docs/it/recipes/string_ids.md",
    "content": "---\ntitle: ID stringa\n---\n\n# ID stringa\n\nQuesta è una delle richieste più frequenti che ricevo, quindi ecco un tutorial sull'uso di String ID.\n\nIsar non supporta nativamente gli ID di stringa e c'è una buona ragione: gli ID interi sono molto più efficienti e veloci. Soprattutto per i collegamenti, l'overhead di un ID stringa è troppo significativo.\n\nComprendo che a volte devi archiviare dati esterni che utilizzano UUID o altri ID non interi. Consiglio di archiviare l'ID String come proprietà nell'oggetto e di utilizzare un'implementazione hash veloce per generare un int a 64 bit che può essere utilizzato come ID.\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nCon questo approccio, ottieni il meglio da entrambi i mondi: ID interi efficienti per i collegamenti e la possibilità di utilizzare ID di stringa.\n\n## Funzione hash veloce\n\nIdealmente, la tua funzione hash dovrebbe avere un'alta qualità (non vuoi collisioni) ed essere veloce. Consiglio di utilizzare la seguente implementazione:\n\n```dart\n/// FNV-1a 64bit hash algorithm optimized for Dart Strings\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nSe scegli una funzione hash diversa, assicurati che restituisca un int a 64 bit ed evita di usare una funzione hash crittografica perché sono molto più lente.\n\n:::warning\nEvita di usare `string.hashCode` perché non è garantito che sia stabile su piattaforme e versioni diverse di Dart.\n:::"
  },
  {
    "path": "docs/docs/it/schema.md",
    "content": "---\ntitle: Schema\n---\n\n# Schema\n\nQuando utilizzi Isar per archiviare i dati della tua app, hai a che fare con le raccolte. Una raccolta è come una tabella di database nel database Isar associato e può contenere solo un singolo tipo di oggetto Dart. Ogni oggetto della raccolta rappresenta una riga di dati nella raccolta corrispondente.\n\nUna definizione di raccolta è chiamata \"schema\". Isar Generator farà il lavoro pesante per te e genererà la maggior parte del codice necessario per utilizzare la raccolta.\n\n## Anatomia di una collezione\n\nDefinisci ogni collezione Isar annotando una classe con `@collection` o `@Collection()`. Una raccolta Isar include campi per ogni colonna nella tabella corrispondente nel database, incluso uno che comprende la chiave primaria.\n\nIl codice seguente è un esempio di una raccolta semplice che definisce una tabella `User` con colonne per ID, nome e cognome:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nPer rendere permanente un campo, Isar deve avervi accesso. Puoi assicurarti che Isar abbia accesso a un campo rendendolo pubblico o fornendo metodi getter e setter.\n:::\n\nCi sono alcuni parametri opzionali per personalizzare la collezione:\n\n| Config        | Description                                                                                                                      |\n| ------------- | -------------------------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Controlla se i campi delle classi padre e dei mixin verranno archiviati in Isar. Abilitato per impostazione predefinita.         |\n| `accessor`    | Consente di rinominare la funzione di accesso predefinita della raccolta (ad esempio `isar.contacts` per la raccolta `Contact`). |\n| `ignore`      | Consente di ignorare determinate proprietà. Questi sono anche rispettati per le super classi.                                    |\n\n### Isar ID\n\nOgni classe di raccolta deve definire una proprietà id con il tipo 'Id' che identifica in modo univoco un oggetto. `Id` è solo un alias per `int` che permette a Isar Generator di riconoscere la proprietà id.\n\nIsar indicizza automaticamente i campi id, il che ti consente di ottenere e modificare gli oggetti in base al loro id in modo efficiente.\n\nPuoi impostare gli ID da solo o chiedere a Isar di assegnare un ID con incremento automatico. Se il campo `id` è `null` e non `finale`, Isar assegnerà un id di autoincremento. Se vuoi un ID di incremento automatico non annullabile, puoi usare `Isar.autoIncrement` invece di `null`.\n\n:::tip\nGli ID di incremento automatico non vengono riutilizzati quando un oggetto viene eliminato. L'unico modo per reimpostare gli ID di incremento automatico è cancellare il database.\n:::\n\n### Rinominare raccolte e campi\n\nPer impostazione predefinita, Isar utilizza il nome della classe come nome della raccolta. Allo stesso modo, Isar utilizza i nomi dei campi come nomi di colonne nel database. Se desideri che una raccolta o un campo abbia un nome diverso, aggiungi l'annotazione `@Name`. L'esempio seguente mostra i nomi personalizzati per la raccolta e i campi:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nSoprattutto se vuoi rinominare i campi o le classi Dart che sono già archiviati nel database, dovresti considerare di usare l'annotazione `@Name`. In caso contrario, il database eliminerà e ricreerà il campo o la raccolta.\n\n### Ignorare i campi\n\nIsar mantiene tutti i campi pubblici di una classe di raccolta. Annotando una proprietà o un getter con `@ignore`, puoi escluderlo dalla persistenza, come mostrato nel seguente frammento di codice:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nNei casi in cui una raccolta eredita i campi da una raccolta padre, di solito è più semplice utilizzare la proprietà `ignore` dell'annotazione `@Collection`:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nSe una collezione contiene un campo con un tipo non supportato da Isar, devi ignorare il campo.\n\n:::warning\nTieni presente che non è buona norma memorizzare informazioni in oggetti Isar che non sono persistenti.\n:::\n\n## Tipi supportati\n\nIsar supporta i seguenti tipi di dati:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nInoltre, sono supportati oggetti incorporati ed enumerazioni. Tratteremo quelli di seguito.\n\n## byte, short, float\n\nPer molti casi d'uso, non è necessario l'intero intervallo di un intero o doppio a 64 bit. Isar supporta tipi aggiuntivi che consentono di risparmiare spazio e memoria durante la memorizzazione di numeri più piccoli.\n\n| Tipo       | Dim. in bytes | Range                                                   |\n| ---------- | ------------- | ------------------------------------------------------- |\n| **byte**   | 1             | 0 to 255                                                |\n| **short**  | 4             | -2,147,483,647 to 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 to 3.4e38                                       |\n| **double** | 8             | -1.7e308 to 1.7e308                                     |\n\nI tipi di numeri aggiuntivi sono solo alias per i tipi Dart nativi, quindi usare `short`, ad esempio, funziona come usare `int`.\n\nEcco una raccolta di esempio contenente tutti i tipi di cui sopra:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nTutti i tipi di numeri possono essere utilizzati anche negli elenchi. Per memorizzare i byte, dovresti usare `List<byte>`.\n\n## Tipi annullabili\n\nComprendere come funziona l'annullamento dei valori in Isar è essenziale: i tipi numerici **NON** hanno una rappresentazione `null` dedicata. Viene invece utilizzato un valore specifico:\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`, `String` e `List` hanno una rappresentazione `null` separata.\n\nQuesto comportamento consente miglioramenti delle prestazioni e ti consente di modificare liberamente la capacità di Null dei tuoi campi senza richiedere la migrazione o codice speciale per gestire i valori `null`.\n\n:::warning\nIl tipo `byte` non supporta valori nulli.\n:::\n\n## DateTime\n\nIsar non memorizza le informazioni sul fuso orario delle tue date. Invece, converte `DateTime`s in UTC prima di archiviarli. Isar restituisce tutte le date nell'ora locale.\n\nI `DateTime`s vengono archiviati con una precisione di microsecondi. Nei browser è supportata solo la precisione in millisecondi a causa delle limitazioni di JavaScript.\n\n## Enum\n\nIsar consente di archiviare e utilizzare le enumerazioni come altri tipi di Isar. Devi scegliere, tuttavia, come Isar deve rappresentare le enumerazioni sul disco. Isar supporta quattro diverse strategie:\n\n| EnumType    | Descrizione                                                                                                    |\n| ----------- | -------------------------------------------------------------------------------------------------------------- |\n| `ordinal`   | TL'indice dell'enum è memorizzato come `byte`. Questo è molto efficiente ma non consente enumerazioni nullable |\n| `ordinal32` | L'indice dell'enumerazione viene archiviato come `short` (4-byte integer).                                     |\n| `name`      | Il nome dell'enumerazione viene memorizzato come `String`.                                                     |\n| `value`     | Per recuperare il valore dell'enumerazioni viene utilizzata una proprietà personalizzata.                      |\n\n:::warning\n`ordinal` e `ordinal32` dipendono dall'ordine dei valori dell'enumerazione. Se modifichi l'ordine, i database esistenti restituiranno valori errati.\n:::\n\nDiamo un'occhiata a un esempio per ciascuna strategia.\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // same as EnumType.ordinal\n  late TestEnum byteIndex; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nNaturalmente, Enums può essere utilizzato anche nelle liste.\n\n## Oggetti incorporati\n\nSpesso è utile avere oggetti nidificati nel modello di raccolta. Non c'è limite a quanto in profondità puoi annidare gli oggetti. Tieni presente, tuttavia, che l'aggiornamento di un oggetto profondamente nidificato richiederà la scrittura dell'intero albero degli oggetti nel database.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nGli oggetti incorporati possono essere nulli ed estendere altri oggetti. L'unico requisito è che siano annotati con `@embedded` e abbiano un costruttore predefinito senza parametri richiesti.\n"
  },
  {
    "path": "docs/docs/it/transactions.md",
    "content": "---\ntitle: Transazioni\n---\n\n# Transazioni\n\nIn Isar, le transazioni combinano più operazioni di database in un'unica unità di lavoro. La maggior parte delle interazioni con Isar utilizza implicitamente le transazioni. L'accesso in lettura e scrittura in Isar è conforme a [ACID](http://en.wikipedia.org/wiki/ACID). Le transazioni vengono automaticamente annullate se si verifica un errore.\n\n## Transazioni esplicite\n\nIn una transazione esplicita, ottieni uno snapshot coerente del database. Cerca di ridurre al minimo la durata delle transazioni. È vietato effettuare chiamate di rete o altre operazioni di lunga durata in una transazione.\n\nLe transazioni (in particolare le transazioni di scrittura) hanno un costo e dovresti sempre provare a raggruppare le operazioni successive in un'unica transazione.\n\nLe transazioni possono essere sincrone o asincrone. Nelle transazioni sincrone è possibile utilizzare solo operazioni sincrone. Nelle transazioni asincrone, solo operazioni asincrone.\n\n|              | Read         | Read & Write       |\n|--------------|--------------|--------------------|\n| Synchronous  | `.txnSync()` | `.writeTxnSync()`  |\n| Asynchronous | `.txn()`     | `.writeTxn()`      |\n\n\n### Transazioni di lettura\n\nLe transazioni di lettura esplicita sono facoltative, ma consentono di eseguire letture atomiche e fare affidamento su uno stato coerente del database all'interno della transazione. Internamente Isar utilizza sempre transazioni di lettura implicita per tutte le operazioni di lettura.\n\n:::tip\nLe transazioni di lettura asincrone vengono eseguite in parallelo ad altre transazioni di lettura e scrittura. Abbastanza bello, vero?\n:::\n\n### Transazioni di scrittura\n\nA differenza delle operazioni di lettura, le operazioni di scrittura in Isar devono essere racchiuse in una transazione esplicita.\n\nQuando una transazione di scrittura viene completata correttamente, viene automaticamente salvata e tutte le modifiche vengono scritte su disco. Se si verifica un errore, la transazione viene interrotta e tutte le modifiche vengono annullate. Le transazioni sono \"tutto o niente\": o tutte le scritture all'interno di una transazione hanno esito positivo o nessuna di esse ha effetto per garantire la coerenza dei dati.\n\n:::warning\nQuando un'operazione di database ha esito negativo, la transazione viene interrotta e non deve più essere utilizzata. Anche se catturi l'errore in Dart.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: move loop inside transaction\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/it/tutorials/quickstart.md",
    "content": "---\ntitle: Avvio rapido\n---\n\n# Avvio rapido\n\nSanti numi, sei qui! Iniziamo a usare il database Flutter più interessante in circolazione...\n\nIn questa guida introduttiva saremo a corto di parole e veloci nel codice.\n\n## 1. Aggiungi dipendenze\n\nPrima che inizii il divertimento, dobbiamo aggiungere alcuni pacchetti a `pubspec.yaml`. Possiamo usare il pub per facilitarci il lavoro pesante.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. Annota le classi\n\nAnnota le tue classi collection con `@collection` e scegli un campo `Id`.\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // puoi anche usare id = null per incrementare automaticamente\n\n  String? name;\n\n  int? age;\n}\n```\n\nGli ID identificano in modo univoco gli oggetti in una collezione e ti consentono di ritrovarli in seguito.\n\n## 3. Esegui il generatore di codice\n\nEsegui il seguente comando per avviare `build_runner`:\n\n```\ndart run build_runner build\n```\n\nSe stai usando Flutter, usa quanto segue:\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Apri l'istanza Isar\n\nApri una nuova istanza Isar e passa tutti i tuoi schemi di raccolte. Facoltativamente, puoi specificare un nome di istanza e una directory.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. Scrivi e leggi\n\nUna volta aperta l'istanza, puoi iniziare a utilizzare le raccolte.\n\nTutte le operazioni CRUD di base sono disponibili tramite \"IsarCollection\".\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // insert & update\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // get\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // delete\n});\n```\n\n## Altre risorse\n\nSei uno studente visivo? Guarda questi video per iniziare con Isar:\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>"
  },
  {
    "path": "docs/docs/it/watchers.md",
    "content": "---\ntitle: Osservatori\n---\n\n# Osservatori\n\nIsar permette di sottoscrivere le modifiche al database. Puoi \"osservare\" le modifiche in un oggetto specifico, un'intera raccolta o una query.\n\nGli osservatori consentono di reagire in modo efficiente alle modifiche nel database. Ad esempio, puoi ricostruire la tua interfaccia utente quando viene aggiunto un contatto, inviare una richiesta di rete quando un documento viene aggiornato, ecc.\n\nUn osservatore riceve una notifica dopo che una transazione è stata eseguita correttamente e la destinazione cambia effettivamente.\n\n## Osservare gli oggetti\n\nSe vuoi essere avvisato quando un oggetto specifico viene creato, aggiornato o eliminato, dovresti osservare un oggetto:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\nCome puoi vedere nell'esempio sopra, l'oggetto non deve ancora esistere. L'osservatore riceverà una notifica quando verrà creato.\n\nC'è un parametro aggiuntivo `fireImmediately`. Se lo imposti su `true`, Isar aggiungerà immediatamente il valore corrente dell'oggetto allo stream.\n\n### Osservazione pigra\n\nForse non è necessario ricevere il nuovo valore ma solo essere avvisati della modifica. Ciò evita a Isar di dover recuperare l'oggetto:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## Osservare le raccolte\n\nInvece di guardare un singolo oggetto, puoi guardare un'intera raccolta e ricevere una notifica quando un oggetto viene aggiunto, aggiornato o eliminato:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## Osservare le query\n\nÈ anche possibile osservare intere query. Isar fa del suo meglio per avvisarti solo quando i risultati della query cambiano effettivamente. Non riceverai una notifica se i collegamenti causano la modifica della query. Utilizza un osservatore di raccolta se hai bisogno di essere informato sulle modifiche ai link.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nSe utilizzi query offset & limit o distinte, Isar ti avviserà anche quando gli oggetti corrispondono al filtro ma al di fuori della query, i risultati cambiano.\n:::\n\nProprio come `watchObject()`, puoi usare `watchLazy()` per ricevere una notifica quando i risultati della query cambiano ma non recupera i risultati.\n\n:::danger\nLa ripetizione delle query per ogni modifica è molto inefficiente. Sarebbe meglio se invece utilizzassi un osservatore di raccolta pigro.\n:::\n"
  },
  {
    "path": "docs/docs/ja/README.md",
    "content": "---\nhome: true\ntitle: ホーム\nheroImage: /isar.svg\nactions:\n  - text: さっそく始めよう！\n    link: /ja/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Flutterのために\n    details: 最小限のSetup、簡単に使えて、追加の設定やボイラープレートは不要。数行のコードを追加後にすぐに使用可能。\n  - title: 🚀 高い拡張性\n    details: 数十万件のレコードを1つのNoSQLデータベースに格納し、効率的かつ非同期にクエリを実行。\n  - title: 🍭 豊富な機能\n    details: 複合 & 複数条件対応インデックスやクエリ修飾子、JSONのサポートなど、データ管理を支援する豊富な機能を搭載。\n  - title: 🔎 全文検索機能\n    details: 全文検索機能を保持。複数の条件を設定したIndexを作成し、簡単にレコードを検索する事が可能。\n  - title: 🧪 ACID セマンティクス\n    details: IsarはACIDに準拠しており、トランザクションを自動的に処理。エラーが発生しても変更をロールバック。\n  - title: 💃 静的型付け\n    details: Isarのクエリは静的型付けされ、コンパイル時にチェックされます。実行時エラーを心配する必要はありません。\n  - title: 📱 マルチプラットフォーム対応\n    details: iOS, Android, Desktop, そしてWEBにも対応！\n  - title: ⏱️ 非同期処理\n    details: 並列クエリ操作とMulti-Isolateをすぐに利用可能。\n  - title: 🦄 オープンソース\n    details: すべてがオープンソースで、永久に無料！\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/ja/crud.md",
    "content": "---\ntitle: CRUD操作\n---\n\n# CRUD操作\n\nコレクションを定義したら、それを操作する方法を学びましょう。\n\n## Isarを開く\n\n何をするにしても、まずはIsarのインスタンスが必要です。各インスタンスには、データベースファイルを格納することができる書き込み権限のあるディレクトリが必要になります。ディレクトリを指定しない場合、Isarは現在のプラットフォームに適したデフォルトのディレクトリを見つけます。\n\nIsarインスタンスで使用したいすべてのスキーマを指定します。複数のインスタンスを開いている場合でも、それぞれのインスタンスに同じスキーマを与える必要があります。\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [ContactSchema],\n  directory: dir.path,\n);\n```\n\n加えて、デフォルト設定を使用するか、もしくは以下のいくつかのパラメータを指定することができます:\n\n| Config |  Description |\n| -------| -------------|\n| `name` | 複数のインスタンスを別々の名前で開きます。デフォルトでは、`\"default\"` が使用されます。 |\n| `directory` | このインスタンスの保存場所です。相対パスまたは絶対パスを渡すことができます。デフォルトでは、iOS では `NSDocumentDirectory` が、Android では `getDataDirectory` が使用されます。Webにおいては必要ありません。 |\n| `relaxedDurability` | 書き込み性能を向上させるために耐久性保証を緩和します。 アプリケーションのクラッシュではなく、システムクラッシュの場合、最後にコミットしたトランザクションが失われる可能性があります。破損する可能性はありません。 |\n| `compactOnLaunch` | インスタンスを開く際にデータベースの圧縮を行うかどうかを確認するための条件です。 |\n| `inspector` | デバッグビルドでインスペクターを有効にします。プロファイルとリリースビルドでは、このオプションは無視されます。 |\n\n既にインスタンスが開かれている場合に `Isar.open()` を呼び出すと、指定したパラメータに関係なく既存のインスタンスを取得します。これはIsarをアイソレートで使用する場合に便利です。\n\n:::tip\nすべてのプラットフォームで有効なパスを取得するために、[path_provider](https://pub.dev/packages/path_provider)パッケージの使用を検討してください。\n:::\n\nデータベースファイルの保存場所は `directory/name.isar` です。\n\n## データベースからの読み込み\n\n`IsarCollection` インスタンスを使用して、Isar で指定した型のオブジェクトを検索したり、照会したり、新規に作成したりすることができます。\n\nこれ以降のサンプルコードでは、コレクション `Recipe` が以下のように定義されていると仮定した上で述べて行きます。\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### コレクションの取得\n\nすべてのコレクションは、Isarインスタンスに格納されています。あなたはrecipesコレクションを次のように取得できます:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\n簡単ですよね？ コレクションアクセサを使いたくない場合は、`collection()` メソッドを使うこともできます:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### idを用いたオブジェクトの取得\n\nまだコレクションにデータはありませんが、あるものと仮定して、 `123` という ID の架空のオブジェクトを取得してみましょう。\n\n```dart\nfinal recipe = await recipes.get(123);\n```\n\n`get()` はオブジェクトを含む `Future` を返しますが、オブジェクトが存在しない場合は `null` を返します。 Isar のすべての操作はデフォルトでは非同期ですが、ほとんどの操作には同期処理も対応しています:\n\n```dart\nfinal recipe = recipes.getSync(123);\n```\n\n:::warning\nUIアイソレートでは、非同期バージョンのメソッドをデフォルトで使用する必要があります。ちなみに、Isarは非常に高速なので、多くの場合において同期バージョンを使用しても問題ありません。\n:::\n\n複数のオブジェクトを一度に取得したい場合は、 `getAll()` または `getAllSync()` を使用してください：\n\n```dart\nfinal recipe = await recipes.getAll([1, 2]);\n```\n\n### オブジェクトのクエリ\n\nIDでオブジェクトを取得する代わりに、 `.where()` と `.filter()` を使って特定の条件に一致するオブジェクトのリストを取得することもできます:\n\n```dart\nfinal allRecipes = await recipes.where().findAll();\n\nfinal favouires = await recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ 詳しくはこちら: [クエリ](queries)\n\n## データベースの書き換え\n\nいよいよコレクションを書き換えるときがやってきました！ オブジェクトを作成、更新、削除するには、それぞれの操作をWriteトランザクション内でラップして使用します:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await recipes.get(123)\n\n  recipe.isFavorite = false;\n  await recipes.put(recipe); // 更新操作の実行\n\n  await recipes.delete(123); // 削除操作の実行\n});\n```\n\n➡️ 詳しくはこちら: [トランザクション](transactions)\n\n### オブジェクトの挿入\n\nIsar でオブジェクトを永続化するには、コレクションにオブジェクトを挿入(put)します。\n\nIsar の `put()` メソッドは、そのオブジェクトが既にコレクションに存在するかどうかに応じて、オブジェクトの挿入もしくは更新を行います。\n\nこの時、id フィールドが `null` または `Isar.autoIncrement` の場合、Isar はオートインクリメントの id を使用します。\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await recipes.put(pancakes);\n})\n```\n\nIsarは `id` フィールドがfinalでは無い場合、オブジェクトに自動的にidを割り当てます。\n\n複数のオブジェクトを一度に挿入することも簡単です。\n\n```dart\nawait isar.writeTxn(() async {\n  await recipes.putAll([pancakes, pizza]);\n})\n```\n\n### オブジェクトの更新\n\n作成と更新の両方は `collection.put(object)` で行います。id が `null` (または存在しない) 場合はオブジェクトは挿入され、そうでない時は更新されます。\n\nつまり、pancakesをunfavoriteにしたい場合は、以下のようになります:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await recipes.put(recipe);\n});\n```\n\n### オブジェクトの削除\n\nオブジェクトを削除したい場合は、`collection.delete(id)`を使用してください. delete メソッドは、指定された id を持つオブジェクトを見つけて、それを削除したかどうかを返します。例えば、id が `123` のオブジェクトを削除したい場合、以下のようになります。\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\ngetやputと同様に、削除されたオブジェクトの数を返す一括削除命令も存在します：\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\n削除したいオブジェクトのidが分からない場合は、クエリを使用することができます:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/ja/faq.md",
    "content": "---\ntitle: よくある質問\n---\n\n# よくある質問\n\nIsarとFlutterのデータベースについてよくある質問を無作為に集めました。\n\n### 何でデータベースが必要なの？\n\n> 私はバックエンドDBにデータを保存しているけど, 何でIsarが必要なの？\n\n地下鉄や飛行機に乗っているとき、あるいはWiFiがなく携帯の電波が非常に悪いおばあちゃんの家に行ったときなど、今日においてもデータ通信ができないことはよくあることです。接続が悪いとアプリ自体が使えないなんてことがあってはなりません。\n\n### Isar vs Hive\n\n答えは簡単です: Isar は [Hiveの代替としてスタート](https://github.com/hivedb/hive/issues/246) し、現在はHiveよりもIsarを使うことを推奨する状態になっています。\n\n### WHERE節って！？\n\n> 何でwhere節にどのインデックスを使うのか選択しなきゃいけないの？\n\nこれには複数の理由があります。ほとんどのデータベースは、与えられたクエリに対して最適なインデックスを選択するためにヒューリスティックを使用しています。データベースは追加の利用状況データを収集する必要があり、それがオーバーヘッドに繋がります。 加えて、それでも間違ったインデックスを選択する可能性があります。 更にはクエリの作成が遅くなるという点も懸念されます。\n\n開発者であるあなた以上に、あなたのデータを知っている人はいません。ですから、あなた自身が最適なインデックスを選択し、例えば、クエリにインデックスを使うかソートにインデックスを使うかなどを決定することができるのです。\n\n### インデックスやwhere節を必ず使わないといけないの？\n\nいえ、必ずしもそんなことはありません。多くの場合、filterしか使用しなかったとしてもIsarは高速で処理されます。\n\n### Isarって速いの？\n\nIsarは、モバイル向けデータベースの中では最速クラスなので、ほとんどのケースでは十分な速度が出るはずです。もし、パフォーマンスで問題が発生した場合は、何か間違った操作をしている可能性があります。\n\n### Isarはアプリのサイズを増加させますか？\n\nそうですね、少しは。Isarは、アプリのダウンロードサイズを約1〜1.5MB増加させます。Isar Webは、数KBの追加にとどまります。\n\n### ドキュメントの内容が間違ってるよ / タイポ見つけた\n\nすみません、失礼致しました。 [issueを開く](https://github.com/isar-community/isar/issues/new/choose)か、もしくは、プルリクエストをして修正して頂けると助かります💪\n"
  },
  {
    "path": "docs/docs/ja/indexes.md",
    "content": "---\ntitle: インデックス\n---\n\n# インデックス\n\nインデックスは、Isarの最も強力な機能です。多くの組み込み型データベースは、\"通常の\"インデックスを提供していますが、Isarは複合インデックスやマルチエントリーインデックスも提供しています。\nクエリのパフォーマンスを最適化するためには、インデックスがどのように機能するかを理解することが重要です。 Isarでは、どのインデックスを、どのように使用するか事を選ぶ事が出来ます。\n\nそれではまず最初に、インデックスとは何かということを簡単に紹介します。\n\n## インデックスとは？\n\nコレクションにインデックスがない場合、行の順番はクエリによって最適化されていない可能性が高く、クエリはオブジェクトを直線的に検索しなければならなくなります。 言い換えれば、クエリはすべてのオブジェクトを検索して、条件にマッチするものを見つけなければならないのです。ご想像のとおり、これには時間がかかります。オブジェクトをひとつひとつ見ていくのは、あまり効率的ではありません。\n\n例えば、この `Product` コレクションは完全に順不同です。\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**データ:**\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\n30ユーロ以上の商品をすべて探そうとするクエリは、9行すべてを検索しなければなりません。9行では問題ないかもしれませんが、10万行になると問題になるでしょう。\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nこのクエリの性能を向上させるために、`price` プロパティにインデックスを付けます。インデックスとは、ソートされた検索テーブルのようなものです。:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**生成されたインデックス:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nこれで、クエリの実行がかなり速くなりました。エクゼキュータは、最後の3つのインデックス行に直接ジャンプして、対応するオブジェクトをそのIDで見つけることができます。\n\n### ソート\n\nもうひとつ素晴らしいのは、インデックスを使うと超高速でソートができることです。ソートを指示するクエリは、ソートをする前にデータベースが全ての結果をメモリにロードする必要があるため、コストがかかります。offsetやlimitを指定しても、それはソート後に適用されます。\n\n例えば、最も安い商品を4つ見つけたいとします。次のようなクエリを使うことができます：\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nこの例では、データベースはすべての(！)オブジェクトを読み込み、それらを価格順にソートして、最も安い価格の 4 つの製品を返さなければなりません。\n\n想像がつくと思いますが、これは先ほどのインデックスを使えばもっと効率的に行えます。データベースはインデックスの最初の4行を受け取り、対応するオブジェクトを返します。なぜなら、これらはすでに適切な順番になっているからです。\n\nインデックスをソートに使うには、次のようなクエリを書きます。\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\n`.anyX()` というwhere節は、Isarにソートのためだけにインデックスを使用するように指示します。また、`priceGreaterThan()`のようなwhere節を使用して、ソートされた結果を得ることもできます。\n\n## ユニークインデックス\n\nユニークインデックス(一意なIndex)は、インデックスが重複した値を含まないことを保証します。これは、1つまたは複数のプロパティで構成されることがあります。ユニークインデックスが1つのプロパティを持つ場合、このプロパティの値は一意となります。ユニークインデックスが複数のプロパティを持つ場合、これらのプロパティの値の組み合わせは一意になります。\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nユニークインデックスに重複を引き起こすデータを挿入または更新しようとすると、エラーになります:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// 同じユーザー名でユーザーを挿入しようとする。\nawait isar.users.put(user2); // -> エラー: 一意制約に反しています。\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## インデックスの置き換え\n\n一意性制約に違反した場合にエラーを投げることが好ましくない場合もあります。エラーを投げる代わりに、既存のオブジェクトを新しいオブジェクトに置き換えたい場合があるかもしれまん。その場合は、インデックスの `replace` プロパティを `true` に設定することで実現できます。\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nこれで、既存のユーザー名でユーザーを挿入しようとすると、Isarは既存のユーザーを新しいユーザーで置き換えます。\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nReplaceインデックスは `putBy()` メソッドも生成し、オブジェクトを置き換えるのではなく、更新することができます。既存の ID は再利用され、リンクはそのまま反映されます。\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// ユーザーが存在しないので、put()と同じです。\nawait isar.users.putByUsername(user1); \nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\n見ての通り、最初に挿入されたユーザーのidが再利用されています。\n\n## 大文字小文字を区別しないインデックス\n\n`String` と `List<String>` プロパティに対するすべてのインデックスは、デフォルトで大文字と小文字を区別して表示されます。大文字小文字を区別しないインデックスを作成したい場合は、 `caseSensitive` オプションを使用することができます。\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## インデックスの種類\n\nインデックスにはさまざまな種類があります。ほとんどの場合、 `IndexType.value` インデックスを使用することになるでしょうが、Hashインデックスを使用するとより効率的です。\n\n### Valueインデックス\n\nValueインデックスは既定の型であり、StringやListを保持しないすべてのプロパティで許可される唯一のものです。インデックスを構築するために、プロパティの値が使用されます。Listの場合は、Listの要素が使用されます。これは、3つのインデックスタイプの中で最も柔軟性がありますが、ストレージも消費します。\n\n:::tip\n基本データ型や、Strings(※where節において `startsWith()` を使いたい場合)、そしてLists（※個別の要素を検索したい場合)においてはIndexType.valueを使用しましょう。\n:::\n\n### Hashインデックス\n\n文字列やListをハッシュ化することで、インデックスに必要なストレージを大幅に削減することができます。Hashインデックスの欠点は、接頭辞の走査 (where節における `startsWith` ) に使用できないことです。\n\n:::tip\n文字列やListに対して、 `startsWith` や `elementEqualTo` という where 節が必要ない場合は、 `IndexType.hash` を使用しましょう。\n:::\n\n### HashElementsインデックス\n\n文字列Listは、全体を (`IndexType.hash` を用いて) ハッシュ化することができますし、Listの要素を個別に (`IndexType.hashElements` を用いて) ハッシュ化して、効率的に要素をハッシュ化したマルチエントリーインデックスを作成することができます。\n\n:::tip\n`List<String>` で `elementEqualTo` の where 節が必要な場合は、 `IndexType.hashElements` を使用します。\n:::\n\n## 複合インデックス\n\n複合インデックスとは、複数のプロパティに対するインデックスのことです。Isarでは、最大3つのプロパティのコンポジットインデックスを作成することができます。\n\n複合インデックスは、複数列インデックスとも呼ばれます。\n\nまず、例から始めるのが一番でしょう。person コレクションを作成し、age プロパティと name プロパティに複合インデックスを定義します。\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**データ：**\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**生成されたインデックス：**\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\n生成された複合インデックスは、年齢と名前でソートされたすべての人物を含んでいます。\n\n複合インデックスは、複数のプロパティでソートされた効率的なクエリを作成したい場合に最適です。また、複数のプロパティを持つ高度な where 節も作成できます。\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\n複合インデックスの末尾のプロパティは、 `startsWith()` や `lessThan()` といった条件もサポートします。\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## マルチエントリーインデックス\n\nIndexType.valueを使ってListのインデックスを作成すると、Isarは自動的にマルチエントリーのインデックスを作成し、List内の各項目がオブジェクトに対してインデックスされます。これはすべての型のListに対して機能します。\n\nマルチエントリーインデックスの実用的な用途としては、タグのListのインデックス化や全文インデックスの作成などが挙げられます。\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` は [Unicode Annex #29](https://unicode.org/reports/tr29/) の仕様に従って文字列を単語に分割するので、ほとんどすべての言語に対して正しく動作します。\n\n**データ:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\n重複する単語を含むエントリは、インデックスに一度だけ表示されます。\n\n**生成されたインデックス:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nこれで、このインデックスは、個々の単語の接頭辞（または等号）をwhere節に使用できるようになりました。\n\n:::tip\n単語を直接保存する代わりに、[Soundex](https://en.wikipedia.org/wiki/Soundex) のような [音声アルゴリズム](https://en.wikipedia.org/wiki/Phonetic_algorithm) を使用する事も候補に入れてみてください。\n:::\n"
  },
  {
    "path": "docs/docs/ja/limitations.md",
    "content": "# 制限事項\n\nご存知のように、Isarはモバイル端末や VM上で動作するデスクトップ、そしてWeb上で動作します。この2つのプラットフォームは非常に異なっており、それぞれ異なる制限事項があります。\n\n## VMの制限事項\n\n- 文字列の最初の1024バイトのみがwhere節の接頭辞として使用可能です。\n- オブジェクトのサイズは 16MB までとなります。\n\n## Webの制限事項\n\nIsar WebはIndexedDBに依存しているため、より多くの制限事項がありますが、 Isarを使用している間はほとんど気にはならないでしょう。\n\n- 同期メソッドはサポートされていません。\n- 現時点において、 `Isar.splitWords()`と`.matches()`フィルターは未実装です。\n- スキーマの変更はVMほど厳密にはチェックされないので、規則に従うように注意してください。\n- すべての数値型は double (唯一の js 数値型) として保存されるので、 `@Size32` は影響しません。\n- インデックスの表現が異なるため、Hashインデックスの容量は減りません。(機能/動作は同じです）\n- `col.delete()` と `col.deleteAll()` は正しく動作しますが、戻り値が正しくありません。\n- `col.clear()` はオートインクリメント値をリセットしません。\n- 値として `NaN` はサポートされていません。\n"
  },
  {
    "path": "docs/docs/ja/links.md",
    "content": "---\ntitle: リンク\n---\n\n# リンク\n\nリンクは、例えばコメントの作成者(User)のようなオブジェクト間の関係を表現することができ、 `1:1`、`1:n`、`n:n`の関係を IsarLinkで表現することができます。リンクの使用は、埋め込みオブジェクトの使用よりも人間工学的に劣るので、可能な限り埋め込みオブジェクトを使用するようにしましょう。\n\nリンクはリレーションを含む別のテーブルだと考えてください。これはSQLのリレーションと似ていますが、異なった機能セットとAPIを持っています。\n\n## IsarLink\n\n`IsarLink<T>` は関連するオブジェクトを含まないか、1つだけ含むことができ、一対一の関係を表現するために使用することができます。`IsarLink` はリンク先のオブジェクトを保持する `value` というプロパティをひとつだけ持っています。\n\nリンクは遅延(lazy)する為、 `IsarLink` に対して、明示的に `value` を読み込みまたは保存するように指示する必要があります。これは、 `linkProperty.load()` と `linkProperty.save()` を呼び出すことで実現できます。\n\n:::tip\nLinkの元(Source)コレクションと対象(Target)コレクションの id プロパティは非final値にすべきです。\n:::\n\nWeb 以外の対象ついては、リンクは初めて使用する際に自動的に読み込まれます。まずは、IsarLinkをコレクションに追加してみましょう。\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\n教師と生徒を介するリンクを定義しました。この例では、全ての生徒が1人だけの教師を持つことができます。\n\nまず、教師を作成し、生徒に割り当てます。教師を `.put()` して、手動でリンクを保存する必要があります。\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\nこれでリンクが使えるようになりました：\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\n同じことを同期コードで試してみましょう。`.putSync()`が自動的にすべてのリンクを保存するので、手動でリンクを保存する必要はありません。さらには、教師も自動作成してくれます。\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\n前の例の生徒が複数の教師を持つことができれば、理にかなっていますよね。幸いなことに、Isarには `IsarLinks<T>` があり、複数の関連オブジェクトを含むことができ、対多関係を表現することができます。\n\n`IsarLinks<T>` は `Set<T>` を継承しており、Setに対して許可されている全てのメソッドを実装しています。\n\n`IsarLinks` は `IsarLink` と同じように動作し、遅延(lazy)します。リンクされたオブジェクトを全て読み込むには、 `linkProperty.load()` を呼び出します。変更を持続させるには、 `linkProperty.save()` を呼び出します。\n\n内部的には、`IsarLink` と `IsarLinks` は同じように表現されています。先ほどの `IsarLink<Teacher>` を `IsarLinks<Teacher>` にアップグレードすれば、（データを失うことなく）一人の生徒に複数の教師を割り当てることができます。\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nリンクの名前（`teacher`）を変更していないので、Isarがそれを記憶しています。\nその為、データを失わずに機能します。\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Backlinks\n\n逆方向の関係を表現したい場合はどうすればいいのでしょうか？安心してください。これからバックリンクを紹介します。\n\nバックリンクとは、逆方向のリンクのことです。各リンクは、常に暗黙のバックリンクを持っています。`IsarLink` や `IsarLinks` に `@Backlink()` というアノテーションをつけることで、アプリでバックリンクを利用できるようになります。\n\nバックリンクは追加のメモリやリソースを必要としません。データを失うことなく、自由に追加、削除、名前の変更を行うことができます。\n\n特定の教師がどのような生徒を持っているかを知りたいので、バックリンクを定義します。\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nまた、バックリンクが指し示すリンクを明示する必要があります。２つのオブジェクトの間に複数の異なるリンクを設定することが可能です。\n\n## リンクの初期化\n\n`IsarLink` と `IsarLinks` にはゼロ引数のコンストラクタがあり、オブジェクトの生成時にリンクのプロパティを代入するために使用されます。リンクのプロパティを `final` にするのは良い習慣です。\n\nオブジェクトを初めて `put()` したとき、リンクは元(Source)コレクションと対象(Target)コレクションで初期化され、 `load()` や `save()` といったメソッドを呼び出すことができるようになります。リンクは作成後すぐに変更の追跡を開始するので、リンクが初期化される前でもリレーションを追加したり削除したりすることができます。\n\n:::danger\nリンクを他のオブジェクトに移動することは不正(illegal)です。\n:::\n"
  },
  {
    "path": "docs/docs/ja/queries.md",
    "content": "---\ntitle: クエリ\n---\n\n# クエリ\n\nクエリとは、ある条件に合致するレコードを探し出す方法です。例えば:\n\n- 星付きの連絡先をすべて検索\n- 連絡先の名前を個別に検索する\n- 姓が定義されていないすべての連絡先を削除する\n\nクエリはDart内ではなくデータベース上で実行されるため、非常に速く実行することができます。インデックスを巧みに使えば、クエリの性能をさらに向上させることができます。\n以降では、クエリの記述方法と、クエリを可能な限り高速化する方法について学びます。\n\nレコードを絞り込むには、2種類の方法があります。フィルタとWHERE節です。まず、フィルターがどのように機能するかを見てみましょう。\n\n## フィルタ\n\nフィルタは使いやすく、わかりやすいです。プロパティの種類に応じて、さまざまなフィルタリング処理が用意されており、そのほとんどが一目でわかるような名前になっています。\n\nフィルタは、フィルタリングされるコレクション内のすべてのオブジェクトに対して評価式を適用することで動作します。式の結果が `true` であった場合、Isar はそのオブジェクトを結果に含めます。フィルタは結果の順序に影響を与えません。\n\nこれから紹介する例では、次のようなモデルを使用します:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### クエリの条件\n\nフィールドの種類に応じて、利用可能な条件が異なります。\n\n| Condition | Description |\n| ----------| ------------|\n| `.equalTo(value)` | 指定した `value` と等しい値に一致する。 |\n| `.between(lower, upper)` | `lower` と `upper` の間にある値に一致する。 |\n| `.greaterThan(bound)` | `bound` よりも大きい値に一致する。 |\n| `.lessThan(bound)` | `bound` よりも小さい値に一致する。デフォルトでは `null` の値も含まれる。なぜなら `null` は他のどの値よりも小さいとみなされるからである。 |\n| `.isNull()` | `null` に一致する。|\n| `.isNotNull()` | `null` ではない値に一致する。|\n| `.length()` | List、String、linkの長さのクエリは、Listやlinkの要素数に基づいてオブジェクトをフィルタリングする。 |\n\nここでは、データベースにsizeが39、40、46のshoeとサイズが設定されていない（`null`）1つのshoeの合計４つが含まれていると仮定します。ソートを行わない限り、値は id でソートされて返されます。\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### 論理演算子\n\n以下の論理演算子を使って述語を合成することもできます:\n\n| Operator   | Description |\n| ---------- | ----------- |\n| `.and()`   | 左側と右側の式の両方が `true` と評価された場合、`true` と評価される。|\n| `.or()`    | どちらかの式が `true` と評価された場合、`true` と評価される。|\n| `.xor()`   | ちょうど1つの式が `true` と評価される場合に、 `true` と評価される。 |\n| `.not()`   | 次の式の結果を否定する。 |\n| `.group()` | 条件をグループ化し、評価順序を指定できるようにする。|\n\nsizeが46のshoesをすべて見つけたい場合は、次のようなクエリを使用します。\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\n複数の条件を使用したい場合は、 **論理積** `.and()` や,  **論理和** `.or()` 、 **排他的論理和** `.xor()`を組み合わせることが出来ます。\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // オプション。 フィルターは暗黙的に論理積で結合される.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nこのクエリは次の式と同等です： `size == 46 && isUnisex == true`.\n\nまた、`.group()` を使って条件をグループ化することもできます：\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nこのクエリは次の式と同等です： `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\n条件やグループを否定するには、**論理否定** `.not()` を使用します:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nこのクエリは次の式と同等です： `size != 46 && isUnisex != true`.\n\n### 文字列の条件\n\n上記のクエリ条件に加えて、文字列値にはさらにいくつかの条件を使用することができます。たとえば、正規表現に似たワイルドカードを使用すると、より柔軟な検索が可能になります。\n\n| Condition            | Description                                                       |\n| -------------------- | ----------------------------------------------------------------- |\n| `.startsWith(value)` | 指定した `value` で始まる文字列値に一致する。          |\n| `.contains(value)`   | 指定した `value` を含む文字列値に一致する。          |\n| `.endsWith(value)`   | 指定した `value` で終わる文字列値に一致する。         |\n| `.matches(wildcard)` | 指定した `wildcard` パターンに適合する文字列値に一致する。 |\n\n**大文字小文字を区別する**  \nすべての文字列操作には、オプションで `caseSensitive` パラメータがあり、デフォルトは `true` です。\n\n**ワイルドカード:**  \n[ワイルドカード文字列表現](https://en.wikipedia.org/wiki/Wildcard_character) は、通常の文字に2つの特殊なワイルドカード文字を使用した文字列です。:\n\n- ワイルドカードの `*` は、0個以上の任意の文字に一致します。\n- ワイルドカードの `?` は、任意の文字に一致します。\n  たとえば, ワイルドカード文字列 `\"d?g\"` は `\"dog\"`, `\"dig\"`, および `\"dug\"` にマッチするが、 `\"ding\"`, `\"dg\"`, および `\"a dog\"` にマッチしません。\n\n### クエリ修飾子\n\n時には、ある条件や異なる値に基づいてクエリを作成することが必要な場合があります。Isarは、条件付きクエリを作成するための非常に強力な機能を持っています。:\n\n| Modifier              | Description                                          |\n| --------------------- | ---------------------------------------------------- |\n| `.optional(cond, qb)` | 条件が `true` の場合のみ、クエリを拡張する。 これは、クエリ内のほぼすべての場所で使用することが出来ます。条件付きでソートしたり絞り込む為に用いるなどが使用例です。 |\n| `.anyOf(list, qb)`    | `values` の各値に対してクエリを拡張し、 **論理和** を用いて条件を組み合わせる。 |\n| `.allOf(list, qb)`    | `values` の各値に対してクエリを拡張し、 **論理積** を用いて条件を組み合わせる。 |\n\nこのサンプルでは、optionalを使用してShoesを見つけることができるメソッドを構築しています：\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // sizeFilter != null の場合のみ、フィルタを適用する。\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\n複数の靴のサイズのいずれかを持つ靴をすべて見つけたい場合は、従来のクエリを書くか、 `anyOf()` 修飾子を使うことができます:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nクエリ修飾子は、動的なクエリを構築したい場合に特に有効です。\n\n### リスト\n\nListにおいてもクエリが可能です:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nListの長さ(length)に基づいてクエリを実行できます:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nこれらは、Dartのコード `tweets.where((t) => t.hashtags.isEmpty);` や `tweets.where((t) => t.hashtags.length > 5);` に相当します。また、リストの要素をもとに問い合わせることもできます：\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nこれはDartのコード `tweets.where((t) => t.hashtags.contains('flutter'));` に相当します。\n\n### 埋め込みオブジェクト\n\n組み込みオブジェクトは、Isarの最も便利な機能の一つです。トップレベルオブジェクトと同じ条件で非常に効率的に問い合わせることができます。例えば、次のようなモデルがあるとします：\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nブランド名が `\"BMW\"` で、国名が `\"Germany\"` である車をすべて問い合わせたいとします。これは以下のクエリで実現できます：\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nネストされたクエリは常にグループ化するようにしましょう。上記のクエリは以下のクエリと結果は同じですが、上記のクエリの方がより効率的に動作します:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### リンク\n\nモデルに[リンクもしくはバックリンク](links)が含まれている場合、リンクされたオブジェクトまたはリンクされたオブジェクトの数に基づいてクエリをフィルタリングすることができます。\n\n:::warning\nリンククエリは、Isarがリンクされたオブジェクトを検索する必要があるため、コストがかかることに留意してください。また、代わりに埋め込みオブジェクトを使用することを検討してみてください。\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\n数学または英語の先生を持つ全ての生徒を見つけたいとします:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nリンクフィルターは、少なくとも1つのリンクオブジェクトが条件にマッチすれば、`true`と評価されます。\n\n教師を持たない全ての生徒を検索してみましょう。:\n  \n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\nもしくは:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Where節\n\nWhere節は非常に強力な機能ですが、正しく使用するのは少し難しいかもしれません。\n\nフィルターとは対照的に、where節はスキーマで定義したインデックスを使用してクエリ条件を確認しています。各レコードを個別にフィルタリングするより、インデックスを用いる方がはるかに高速です。\n\n➡️ 詳しくはこちら: [インデックス](indexes)\n\n:::tip\n基本的なルールとして、Where節を使用してレコードをできる限り減らし、残りのフィルタリングはフィルタを使用して行うようにすることをお勧めします。\n:::\n\nwhere節を組み合わせるには、**論理和**しか使えません。言い換えると、複数のwhere節を合計することはできますが、複数のwhere節の交差部分を照会することはできません。\n\nそれではShoeコレクションにインデックスを追加してみましょう:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nここではインデックスが2つあります。`size` のインデックスは、 `.sizeEqualTo()` のような where 節を使用可能にしています。`isUnisex` の複合インデックス(CompositeIndex)は、 `isUnisexSizeEqualTo()` のような where 節を使用できるようにしています。そしてまた、インデックスの接頭辞は常に任意のものを使用できる為、 `isUnisexEqualTo()` のような事も可能です。\n\nこれでサイズ46のユニセックスの靴を検索する以前見たクエリを、複合インデックスを使用して書き換えることができます。このクエリは前記で述べたクエリよりも高速に動作します:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nWhere節には、さらに2つの強力な機能があります。\nWhere節は\"Free\"なソートと、超高速なDISTINCT命令を保持しています。\n\n### where節とフィルタの組み合わせ\n\n`shoes.filter()` というクエリを覚えていますか？\n\n実はこれは `shoes.where().filter()` の短縮形なのです。where節とfilterを同じクエリで組み合わせて、両方の利点を利用することができます（そして、そうすべきです）：\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nまず、where 節が適用され、フィルタリングされるオブジェクトの数が減ります。その後、残りのオブジェクトにフィルタが適用されます。\n\n## ソート\n\nクエリ実行結果のソート方法は、`.sortBy()`, `.sortByDesc()`, `.thenBy()`, `.thenByDesc()` メソッドを用いて定めることが可能です。\n\nインデックスを使わずに、すべての靴をModel名の昇順とSizeの降順でソートして検索する方法です:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\n特に、ソートは offset と limit の前に行われるため、たくさんの結果をソートするのはコストがかかります。上記のソートメソッドでは、インデックスを使用することはありません。幸いなことに、Where節によるソートを使えば、100万個のオブジェクトをソートする場合でもクエリを高速に実行することができます。\n\n### Where節のソート\n\nクエリで **単一(single)** の where 節を使用した場合、結果はすでにインデックスでソートされています。これは非常に重要です。\n\n例えば、サイズ `[43, 39, 48, 40, 42, 45]` の靴があり、サイズが `42` より大きい靴をすべて検索し、サイズ順に並べたいとしましょう。\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // 加えて、結果がSizeでソートされる\n  .findAll(); // -> [43, 45, 48]\n```\n\n見ての通り、結果は `size` インデックスでソートされています。where 節のソート順を逆にしたい場合は、 `sort` に `Sort.desc` をセットします：\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\n時には where 節を使いたくないけれども、暗黙のうちにソートが行われるという恩恵を受けたいこともあるでしょう。そのような場合には、 `any` という where 節を使用します：\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nもし、あなたが複合インデックスを使用した場合、結果はそのインデックス内のすべてのフィールドでソートされます。\n\n:::tip\n結果をソートする必要がある場合は、インデックスを使用することを検討してください。`offset()` や `limit()` を使っている場合は特にそうです。\n:::\n\n時には、ソートのためにインデックスを使用することが出来なかったり、有用ではない場合もあるかもしれません。そのような場合は、インデックスを使用して結果の項目数をできるだけ減らすのが良いでしょう。\n\n## ユニーク値\n\n一意な値を持つ項目のみを返すには、distinct述語を使用します。たとえば、Isar データベースに何種類の異なる靴のModelがあるかを調べるには、 以下のようにします：\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nまた、複数のdistinctの条件を繋げて、異なるModelとSizeの組み合わせである全ての靴を検索することができます。\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\n異なる組み合わせの最初の結果のみが返されます。これをコントロールするために、where句とソート操作を使用することも可能です。\n\n### WHERE節のdistinct\n\n一意でないインデックスがある場合、それの全ての異なる値を取得したい時があると思います。前のセクションで紹介した `distinctBy` オペレーションを使うこともできますが、ソートやフィルタの後に実行されるため、若干のオーバーヘッドが発生します。\nWHERE節を1つだけ使用するのであれば、代わりにインデックスに依拠してdistinct処理を実行することができます。\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\n理論・仕組み的には、ソートとdistinctのために複数のwhere節を使うこともできます。複数のwhere節を使う唯一の制限は、これらのwhere節が重複しておらず、同じインデックスを使用していることです。正しいソートを行うには、ソート順で適用する必要があります。十分に注意をしてください。\n:::\n\n## OffsetとLimit\n\n遅延(lazy)リストビューのために、クエリ結果の数を制限することは良い方法だと思います。これを行うには、 `limit()` を設定します。\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\n`offset()` を設定することで、クエリの結果をページネイト(取得開始位置の指定)することもできます。\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nDartオブジェクトのインスタンス化は、クエリ実行の中で最もコストのかかる部分であることが多いので、必要なオブジェクトだけを読み込むのが良いでしょう。\n\n## 実行順序\n\nIsarは常に同じ順序でクエリーを実行します：\n\n1. プライマリまたはセカンダリインデックスを走査してオブジェクトを見つける（where節の適用）\n2. オブジェクトのフィルタリング\n3. 結果のソート\n4. distinct操作の適用\n5. 結果のoffset と limit\n6. 結果の返却\n\n## クエリの操作\n\nこれまでの例では、`.findAll()` を使ってマッチするオブジェクトをすべて取得しました。しかし、利用できる操作は他にも沢山あります。\n\n| Operation        | Description                                                                                                         |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | 最初にマッチしたオブジェクトのみを取得し、マッチしない場合は `null` を取得する。                                                  |\n| `.findAll()`     | マッチしたオブジェクトを全て取得する。                                                                        |\n| `.count()`       | クエリにマッチするオブジェクトの数を数える。                                                                             |\n| `.deleteFirst()` | コレクションから、最初にマッチしたオブジェクトを削除する。                                                               |\n| `.deleteAll()`   | コレクションから、一致するすべてのオブジェクトを削除する。                                                                    |\n| `.build()`       | クエリをコンパイルして、後で再利用することが出来る。これにより、クエリを複数回実行したい場合に、そのクエリを構築するためのコストを節約する事が出来る。 |\n\n## プロパティクエリ\n\n単一プロパティの値にしか関心が無く必要の無い場合、プロパティクエリを使用することができます。通常のクエリを構築し、プロパティを選択するだけです:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\n単一プロパティのみを使用することで、逆シリアル化の時間を節約できます。プロパティクエリは、埋め込みオブジェクトやリストに対しても機能します。\n\n## アグリゲーション(集約)\n\nIsarはプロパティクエリの値を集約する機能を持っています。以下の集約操作が可能です：\n\n| Operation    | Description                                                    |\n| ------------ | -------------------------------------------------------------- |\n| `.min()`     | 最小値を探す。該当するものがなければ `null` となる。             |\n| `.max()`     | 最大値を探す。該当するものがなければ `null` となる。            |\n| `.sum()`     | 全ての値を合計する。                                               |\n| `.average()` | すべての値の平均を計算し、一致するものがない場合は `NaN` を計算する。 |\n\n一致するオブジェクトをすべて見つけて手動で集約するよりも、アグリゲーションを使用する方が、はるかに高速になります。\n\n## 動的なクエリ\n\n:::danger\nこのセクションは、おそらくほとんどの方には関係ないでしょう。ダイナミッククエリの使用は、どうしても必要な場合（ほぼ無いです）を除き、お勧めしません。\n:::\n\n今まで述べて来たすべての例は、QueryBuilderと生成された静的な拡張メソッドを使用しています。もしかしたら、動的なクエリや（Isar Inspectorのような）カスタムクエリ言語を作りたいかもしれません。その場合は、`buildQuery()` メソッドを使うことができます：\n\n| Parameter       | Description                                                                                 |\n| --------------- | ------------------------------------------------------------------------------------------- |\n| `whereClauses`  | クエリのwhere節                                                             |\n| `whereDistinct` | where 節が個別の値を返すかどうか（単一の where 節の場合のみ有効） |\n| `whereSort`     | where節のトラバース(巡回)順序（単一のwhere節にのみ有効）             |\n| `filter`        | 結果に適用するフィル                                                         |\n| `sortBy`        | ソートするプロパティの一覧                                                            |\n| `distinctBy`    | 区別するプロパティの一覧                                     |\n| `offset`        | 結果のoffset                                                                  |\n| `limit`         | 返送する結果の最大数                                                 |\n| `property`      | nullで無い場合、このプロパティの値のみが返される。                                |\n\nそれでは動的なクエリを作成してみましょう:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nこれらは以下のクエリに相当します。:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/ja/recipes/data_migration.md",
    "content": "---\ntitle: データの移行\n---\n\n# データの移行\n\nコレクション、フィールド、インデックスを追加または削除すると、Isarは自動的にデータベーススキーマを移行(マイグレート)します。時には、データも一緒に移行したい場合もあるでしょう。 Isarは組み込み解決法を提供していません。これはデタラメな移行制限が課される可能性があるためです。ただ、ニーズに合った移行ロジックを簡単に実装することができます。\n\nこの例では、データベース全体で1つのバージョンを使用したいと思います。共有環境設定を使って現在のバージョンを保存し、移行したいバージョンと比較します。バージョンが一致しない場合、データを移行し、バージョンを更新します。\n\n:::tip\n各コレクションに独自のバージョンを与え、個別に移行することも可能です。\n:::\n\n誕生日フィールドを持つユーザーコレクションがあると仮定します。このアプリのバージョン2では、年齢に基づいてユーザーを照会するために、誕生年のフィールドを追加する必要があります。\n\nVersion 1:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersion 2:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\n問題は、バージョン1では `birthYear` フィールドが存在しないため、既存のUserモデルを作成しても空の `birthYear`が設定されることです。 `birthYear` フィールドを設定するために、データを移行する必要があります。\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // バージョンが設定されていない場合(新規インストール)、または既にver.2の場合は移行する必要はない\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // バージョンを更新する\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // すべてのユーザーを一度にメモリにロードするのを避けるため、ユーザーをページ分割する\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // birthYear ゲッターを使用しているため、何も更新する必要はありません\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\n多くのデータを移行する必要がある場合、UIスレッドに負担がかからないようにバックグラウンドアイソレートを使用することを検討してください。\n:::\n"
  },
  {
    "path": "docs/docs/ja/recipes/full_text_search.md",
    "content": "---\ntitle: 全文検索\n---\n\n# 全文検索\n\n全文検索は、データベース内のテキストを検索する強力な方法です。[インデックス](../indexes.md)がどのように機能するかについては既にご存じだとは思いますが、基本的なことを説明します。\n\nインデックスはルックアップテーブルのように機能し、特定の値を持つレコードをクエリエンジンがすばやく検索できるようにします。たとえば、オブジェクトに `title` フィールドがある場合、そのフィールドにインデックスを作成することで、指定したタイトルを持つオブジェクトをより速く見つけることができます。\n\n## なぜ全文検索が便利なのか\n\nIsarDB ではフィルタを使って簡単にテキストを検索することができます。例えば、 `.startsWith()`, `.contains()`, `.matches()` のような様々な文字列操作があります。フィルタの問題は、その実行時間が `O(n)` (ここで `n` はコレクション内のレコードの数) であることです。特に、 `.matches()` のような文字列演算は時間がかかります。\n\n:::tip\n全文検索はフィルタよりはるかに高速ですが、インデックスにはいくつかの制限があります。このレシピでは、これらの制限を回避する方法を探ります。\n:::\n\n## 基本例\n\n考え方としては常に同じです：テキスト全体をインデックス化するのではなく、テキスト中の単語をインデックス化し、個別に検索できるようにします。\n\nそれではさっそく、基本的な全文インデックスを作成してみましょう:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nこれで、content 内の特定の単語を検索できるようになりました:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nこのクエリは高速に動作しますが、いくつかの問題があります：\n\n1. 単語全体しか検索できない\n2. 句読点は考慮しない\n3. 他の空白文字の検索に対応していない\n\n## テキストを正しく分割する\n\n先ほどの例を改善してみましょう。単語分割を修正するために複雑な正規表現を開発しようとすることもできますが、おそらく時間がかかり、エッジケースで間違ってしまう可能性もあります。\n\n[Unicode Annex #29](https://unicode.org/reports/tr29/)では、ほぼ全ての言語について、テキストを単語に正しく分割する方法を定義しています。これは非常に複雑ですが、幸いなことに、Isar は重い仕事を代わりにやってくれます。\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## 他の機能の追加\n\n他の機能も簡単に実装できますよ！プレフィックスマッチングや大文字小文字を区別しないマッチングをサポートするようにインデックスを変更することもできます。\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nデフォルトでは、Isar は単語をハッシュ値として保存します。これは高速で容量効率のよい方法です。 しかし、ハッシュ値はプレフィックスマッチングに使用することはできません。インデックスを変更して、`IndexType.value` を使用すると、単語を直接利用することができます。これによって `.titleWordsAnyStartsWith()` という where 節を提供します。\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## `.endsWith()` の実装\n\n`.endsWith()` の実装も、勿論可能です！ここでは、`.endsWith()`のマッチングを実現するためのちょっとしたテクニックをお見せします。\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\n検索したい語尾を反転(reversed)させることを忘れないようにしてください。\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## ステミングアルゴリズム\n\n残念ながら、インデックスは `.contains()` マッチングをサポートしていません (これは他のデータベースでも同様です)。しかし、いくつかの代替手段があり、検討する価値はあります。その選択肢は、用途に大きく依存します。その一例として、単語全体ではなく、単語の語幹をインデックス化する方法があります。\n\nステミングアルゴリズムは、言語の正規化プロセスで、単語のさまざまな形式を共通の形式に変換します：\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\n一般的なアルゴリズムは、[Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) と [Snowball stemming algorithms](https://snowballstem.org/algorithms/) です。\n\nまた、[lemmatization](https://en.wikipedia.org/wiki/Lemmatisation) のような、より高度な形式もあります。\n\n## 音声学的アルゴリズム\n\n[音声アルゴリズム](https://en.wikipedia.org/wiki/Phonetic_algorithm)とは、発音によって単語を割り出すためのアルゴリズムです。つまり、探している単語と似た音の単語を見つけることができるのです。\n\n:::warning\n音声アルゴリズムの多くは、単一言語しかサポートしていません。\n:::\n\n### Soundex\n\n[Soundex](https://en.wikipedia.org/wiki/Soundex)は、英語の発音で人名を索引付けするための音声アルゴリズムです。同音異義語が同じ表現にエンコードされ、スペルが多少違ってもマッチングできるようにすることが目的で作られています。これは簡単なアルゴリズムであり、複数の改良版が存在する。\n\nこのアルゴリズムを使うと、`\"Robert\"` と `\"Rupert\"` はともに `\"R163\"` という文字列を返し、 `\"Rubin\"` は `\"R150\"` を返します。`Ashcraft\"` と `\"Ashcroft\"` は共に `\"A261\"` を返します。\n\n### Double Metaphone\n\n[Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) 音素符号化アルゴリズムは、このアルゴリズムの第二世代です。このアルゴリズムは、オリジナルの Metaphone アルゴリズムと比較して、いくつかの基本的な設計上の改良がなされています。\n\nDouble Metaphone は、スラブ語、ゲルマン語、ケルト語、ギリシャ語、フランス語、イタリア語、スペイン語、中国語、およびその他の起源の英語におけるさまざまな不規則性を考慮しています。\n"
  },
  {
    "path": "docs/docs/ja/recipes/multi_isolate.md",
    "content": "---\ntitle: Multi-Isolateの使用法\n---\n\n# Multi-Isolateの使用法\n\nスレッドの代わりに、すべてのDartのコードはアイソレートの内部で実行されます。それぞれのアイソレートは独自のメモリヒープを持ち、アイソレート内のどのステートも他のアイソレートからアクセスできないことを保証しています。\n\nIsarは同時に複数のアイソレートからアクセスすることができ、ウォッチャーもアイソレートをまたいで動作します。このレシピでは、複数のアイソレート環境でIsarを使用する方法を確認します。\n\n## いつMulti-Isolateを使用すべきか\n\nIsarのトランザクションは、同じアイソレートで実行されても並列に実行されます。そうだとしても、場合によっては、複数のアイソレートからIsarにアクセスすることが 有益なこともあります。\n\nその理由は、IsarはDartオブジェクトとの間でデータのエンコードとデコードにかなりの時間を費やしているからです。これはJSONのエンコードとデコードのようなものだと考えることができます。（ただ、より効率的です）これらの操作は、データがアクセスされるアイソレートの内部で実行され、当然アイソレート内の他のコードをブロックします。言い換えれば IsarはあなたのDartアイソレートで作業の一部を実行します。\n\n一度に数百のオブジェクトを読み書きする必要があるだけなら、UIアイソレートで行うことは問題ではありません。しかし、巨大なトランザクションや、UIスレッドがすでにBusy状態である場合は、別のアイソレートを使用することを検討する必要があります。\n\n## 具体例\n\nまず最初に行うべきことは、新しいアイソレートでIsarをオープンすることです。Isarのインスタンスは既にメインとなるアイソレートで開かれているので、 `Isar.open()` は同じインスタンスを返します。\n\n:::warning\nメインアイソレートと同じスキーマを提供することを忘れないでください。そうでない場合は、エラーになります。\n:::\n\n`compute()` は Flutter で新しいアイソレートを開始し、その中で与えられた関数を実行します。\n\n```dart\nvoid main() {\n  // UIアイソレートでIsarを開く\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // データベースの変更を監視する\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // 新しいアイソレートを開始し、10000メッセージを作成します。\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // しばらくすると:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// 新しいアイソレート内で実行される関数\nFuture createDummyMessages(int count) async {\n  // インスタンスはすでに開かれているので、ここではPathは必要ありません。\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // アイソレート内で同期トランザクションを使用する。\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\n上記の例の中で、いくつか興味深い点があります:\n\n- `isar.messages.watchLazy()` は UI アイソレートで呼び出されていますが、他のアイソレートからの変更についても通知されている。\n- インスタンスは名前(name)で参照されます。デフォルトの名前は `default` ですが、この例では `myInstance` に設定しました。\n- メッセージを作成するために同期トランザクションを使用しました。新しいアイソレートをブロックすることは問題ありませんし、同期トランザクションは少し速くなります。\n"
  },
  {
    "path": "docs/docs/ja/recipes/string_ids.md",
    "content": "---\ntitle: 文字列のID\n---\n\n# 文字列のID\n\nこのチュートリアルは、私が最も頻繁に受け取るリクエストの一つです。\n\nIsarはString IDを標準サポートしていませんが、それには理由があります。特にリンクの場合、String IDのオーバーヘッドが大きすぎるのです。\n\n時には、UUIDやその他の整数型ではないidを利用する外部データを保存しなければならない場合があることは理解しています。 そこで、String idをオブジェクトのプロパティとして保存し、fastHashを実装して、Idとして使用できる64ビットintを生成することをお勧めします。\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nこの方法を使用すれば、リンク用の効率的な整数IDと、文字列IDを使用する機能という、両方の長所を得ることができます。\n\n## Fast hash 関数\n\nハッシュ関数は高品質で高速なものが理想的です（かつコリジョンは避けたい）。\nそこで、以下のような実装をお勧めします。\n\n```dart\n/// Dart Stringsの為に最適化されたFNV-1a 64bitハッシュアルゴリズム \nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nもし別のハッシュ関数を選択したい場合は、64ビットの整数値を返すことを確認する事に加えて、暗号化ハッシュ関数を使用することは避けてください（処理速度が大幅に低下します）。\n\n:::warning\n`String.hashCode` は、異なるプラットフォームやバージョンの Dart で安定して動作することが保証されていないため、使用しないようにしましょう。\n:::\n"
  },
  {
    "path": "docs/docs/ja/schema.md",
    "content": "---\ntitle: スキーマとは\n---\n\n# スキーマとは\n\nIsar を使用してアプリのデータを保存する場合、コレクションを扱うことになります。コレクションとは、関連付けられた IsarDB 内のテーブルのようなもので、単一型の Dart オブジェクトのみを格納することができます。それぞれのコレクションオブジェクトは、対応するコレクションの行を表します。\n\nコレクション定義は \"スキーマ\"と呼ばれます。Isar Generator は貴方のために手間のかかる面倒な作業を行い、コレクションを使用するのに必要なコードの大部分を生成してくれます。\n\n## コレクションの構造\n\nIsar コレクションを定義するには、Class を `@collection` または `@Collection()` でアノテートします。 Isar コレクションは対応するテーブル内の各列となるフィールドを含みます。ここには、主キーを構成するフィールドも含めてください。\n\n次のコードは、ID、名前、苗字の列を持つ `User` テーブルを定義するシンプルなコレクションの例です。\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nフィールドを永続化するためには、Isar がそのフィールドにアクセスできる必要があります。フィールドを public にしたり、Getter や Setter のメソッドを用意したりすることで、Isar がフィールドにアクセスできるようになります。\n:::\n\nコレクションをカスタマイズするために、いくつかの任意のパラメータがあります：\n\n| Config        | Description                                                                                                                         |\n| ------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | 親クラスや mixins のフィールドを Isar に保存するかどうかを管理します。デフォルトでは有効です。                                      |\n| `accessor`    | デフォルトのコレクションアクセサの名前を変更できるようにします。 (たとえば、`Contact` コレクションには `isar.contacts` を指定など). |\n| `ignore`      | 特定のプロパティを無視(除外)することができます。これらは、スーパークラスに対しても同様に適用されます。                              |\n\n### Isar の Id\n\n各コレクションクラスは、オブジェクトを一意に識別する `Id` 型の id プロパティを定義する必要があります。`Id` は `int` の別名(エイリアス)で、IsarGenerator が id プロパティを識別できるようにするためのものです。\n\nIsar は自動的に id フィールドにインデックスを作成するので、id に基づいて効率的にオブジェクトを取得したり変更したりすることができます。\n\nid は自分で設定することもできますし、Isar にオートインクリメントの id を割り当ててもらうこともできます。もし`id` フィールドが `null` かつ `final` でない場合、Isar はオートインクリメントの id を割り当てます。NULL でないオートインクリメントの id が欲しい場合は、 `null` の代わりに `Isar.autoIncrement` を使用することができます。\n\n:::tip\nオブジェクトが削除された場合、オートインクリメント ID は再利用されません。オートインクリメント ID をリセットする唯一の方法は、データベースを削除(Clear)することです。\n:::\n\n### コレクションとフィールドの名前変更\n\nデフォルトでは、Isar はクラス名をコレクション名として使用します。同様に、Isar はフィールド名をデータベースの列名として使用します。コレクションやフィールドに別の名前を付けたい場合は、 `@Name` アノテーションを追加します。次の例は、コレクションとフィールドの名前をカスタマイズする例です:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\n特に、既にデータベースに保存されている Dart のフィールドやクラスの名前を変更したい場合は、 `@Name` アノテーションの使用を検討する必要があります。そうしないと、データベースがそのフィールドやコレクションを削除したり、再作成したりすることになりかねません。\n\n### フィールドを無視する\n\nIsar は、コレクションクラスのすべての public フィールドを永続化します。プロパティや Getter に `@ignore` というアノテーションを付けると、次のコードスニペットのように永続化から除外することができます:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nコレクションが親コレクションからフィールドを継承しているような場合は、通常、 `@Collection` アノテーションの `ignore` プロパティを使用する方が簡単です:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nもし、コレクションに Isar がサポートしていない型のフィールドが含まれている場合、そのフィールドは無視しなければなりません。\n\n:::warning\n永続化されていない Isar オブジェクトに情報を保存することは、良い習慣ではないことに留意してください。\n:::\n\n## 対応している型\n\nIsar は以下のデータ型に対応しています:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\n加えて、埋め込み型オブジェクトと列挙型(Enum)もサポートされています。それらについては後述します。\n\n## byte, short, float\n\n多くの場合、64 ビット整数型や double の全範囲は必要ありませんよね。Isar は、より小さな数値を保存する際の為に、容量とメモリを節約することができる追加の型をサポートしています。\n\n| Type       | Size in bytes | Range                                                   |\n| ---------- | ------------- | ------------------------------------------------------- |\n| **byte**   | 1             | 0 to 255                                                |\n| **short**  | 4             | -2,147,483,647 to 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 to 3.4e38                                       |\n| **double** | 8             | -1.7e308 to 1.7e308                                     |\n\n追加の数値型は Dart のネイティブ型の別名(エイリアス)に過ぎません。例えば `short` を使用すると、 `int` を使用するのと同じように動作します。\n\n以下に、上記のすべての型を含むコレクションの例を示します：\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nすべての数値型は List でも使用することができます。バイトを格納する場合は、`List<byte>` を使用してください。\n\n## Null 許容型\n\nIsar で nullability(訳注：DB 関連用語では、列などの項目が NULL 値を受け入れる能力)がどのように機能するかを理解するのは非常に重要です：\n\n数値型は、専用の `null` 表現を持ちません。その代わりに、特定の値が使用されます:\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`, `String`, `List` は、それぞれ別の `null` 表現を持ちます。\n\nこの動作によってパフォーマンスが向上し、 `null` 値を処理するためのマイグレーションや特別なコードを必要とせずに、フィールドの nullability を自由に変更することができるようになります。\n\n:::warning\n`byte` 型は null 値をサポートしていません。\n:::\n\n## DateTime\n\nIsar は、日付のタイムゾーン情報を保存しません。その代わり、`DateTime`を UTC に変換してから保存します。Isar はすべての日付をローカルタイムで返します。\n\n`DateTime`はマイクロ秒の精度で保存されます。ただしブラウザ上においては、JavaScript の制限により、ミリ秒の精度しかサポートされていません。\n\n## 列挙型(Enum)\n\nIsar では他の型と同様に、列挙型を保存し使用することができます。しかし、Isar がディスク上でどのように enum を表すかを選択する必要があります。Isar は 4 つの異なる方法をサポートしています。:\n\n| EnumType    | Description                                                                                                           |\n| ----------- | --------------------------------------------------------------------------------------------------------------------- |\n| `ordinal`   | 列挙型のインデックスは `byte` として格納されます。これは非常に効率的ですが、null 値を許容する enum は使用できません。 |\n| `ordinal32` | 列挙型のインデックスは `short` (4 バイトの整数) として格納されます。                                                  |\n| `name`      | 列挙名称は `String` として格納されます。                                                                              |\n| `value`     | 列挙値の取得には、カスタムプロパティを使用します。                                                                    |\n\n:::warning\n`ordinal` と `ordinal32` は、列挙された値の順番に依存します。この順序を変更すると、既存のデータベースは不正な値を返す可能性があります。\n:::\n\nそれでは、それぞれの方法の例を確認してみましょう。\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // EnumType.ordinalと同様\n  late TestEnum byteIndex; // null 許容には出来ない\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // null 許容には出来ない\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nもちろん、Enum は List 内でも使用可能です。\n\n## 組み込みオブジェクト\n\nコレクションモデルでオブジェクトをネストさせると便利なことがよくあります。オブジェクトをネストさせる深さは無制限です。しかし、深くネストされたオブジェクトを更新するには、オブジェクトツリー全体をデータベースに書き込む必要があることを覚えておいてください。\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\n埋め込みオブジェクトは null を許容する事も出来ますし、他のオブジェクトを拡張(extend)することも出来ます。唯一の要件は `@embedded` のアノテーションを付け、required パラメータの無いデフォルトのコンストラクタを持つことです。\n"
  },
  {
    "path": "docs/docs/ja/transactions.md",
    "content": "---\ntitle: トランザクション\n---\n\n# トランザクション\n\nIsarにおいて、トランザクションは複数のデータベース操作を1つの作業単位にまとめます。Isarの大半の処理は、暗黙のうちにトランザクションを利用しています。IsarのRead & write操作は、[ACID](http://en.wikipedia.org/wiki/ACID)に準拠しており、トランザクションは、エラーが発生すると自動的にロールバックされます。\n\n## 明示的なトランザクション\n\n明示的なトランザクションでは、データベースの整合性のあるスナップショットを取得したり、トランザクションの継続時間を最小限にするようにします。また、トランザクションの中でネットワークの呼び出しやその他の長時間実行される操作を行うことは禁じられています。\n\nトランザクション（特に書き込みトランザクション）にはコストがかかるので、連続する操作は常に1つのトランザクションにまとめるようにしましょう。\n\nトランザクションには、同期と非同期があります。同期トランザクションでは、同期操作のみを使用することができて、非同期トランザクションでは、非同期操作のみを使用することができます。\n\n|              | Read         | Read & Write       |\n|--------------|--------------|--------------------|\n| 同期  | `.txnSync()` | `.writeTxnSync()`  |\n| 非同期 | `.txn()`     | `.writeTxn()`      |\n\n\n### 読み取りトランザクション\n\n明示的な読み取りトランザクションは任意ですが、Atomic(原子性の)読み取りを可能にし、トランザクション内のデータベースの一貫した状態に依存することができます。内部的には、Isarはすべての読み込み操作に対して常に暗黙的な読み込みトランザクションを使用します。\n\n:::tip\n非同期読み取りトランザクションは、他の読み取りおよび書き込みトランザクションと並行して実行されます。かなりイケてますよね？\n:::\n\n### 書き込みトランザクション\n\n読み込み操作とは異なり、Isarでの書き込み操作は明示的なトランザクションに包まれる必要があります。\n\n書き込みトランザクションが正常に終了すると、自動的にコミットされ、すべての変更がディスクに書き込まれます。もしエラーが発生した場合、トランザクションは中断され、すべての変更がロールバックされます。トランザクションは \"all or nothing\"です。トランザクション内のすべての書き込みが成功するか、データの一貫性を保証するために何も実行されないかのどちらかである。\n\n:::warning\nデータベース操作に失敗した場合、トランザクションは破棄され、それ以降使用してはいけません。たとえDartでエラーを捕捉したとしてもです。\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: トランザクションの中にforループを移動させましょう。\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/ja/tutorials/quickstart.md",
    "content": "---\ntitle: クイックスタート\n---\n\n# クイックスタート\n\nお待たせしました。さあ、最高にクールなFlutterのデータベースを使い始めましょう！\n\nこの記事では、簡潔にコードを書いていきます。\n\n\n## 1. 依存関係を追加する\n\nIsarを使用する前に、いくつかのパッケージを `pubspec.yaml` に追加する必要があります。pubを使用する事で、面倒な作業を簡単に済ませることが出来ます。\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. クラスの注釈(アノテーション)\n\nあなたの使用するコレクションクラスに `@collection` でアノテーションを付け、`Id` フィールドを設定します。\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // id = nullでも自動インクリメントされます。\n\n  String? name;\n\n  int? age;\n}\n```\n\nidはコレクション内のオブジェクトを一意に識別して、後で再び見つけられるようにします。\n\n## 3. コード生成ツールの実行\n\n以下のコマンドを実行して、`build_runner`を起動します。:\n\n```\ndart run build_runner build\n```\n\nFlutterを使用している場合は、代わりに次のコマンドを使用してください:\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Isarインスタンスを開く\n\n新規のIsarインスタンスを開き、コレクションのスキーマを渡します。必要に応じて、インスタンス名とディレクトリを指定することができます。\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. 書き込みと読み込み\n\nIsarインスタンスを開いたら, コレクションを利用することができます.\n\n基本的なCRUD操作は、全て `IsarCollection` を介して行う事が出来ます。\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // 挿入と更新\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // 取得\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // 削除\n});\n```\n\n## その他の資料\n\n視覚的に学ぶ方が好みであれば、Isarを始めるためにこれらの動画をぜひご覧ください:\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/ja/watchers.md",
    "content": "---\ntitle: ウォッチャー\n---\n\n# ウォッチャー\n\nIsar では、データベースの変更を監視することができます。特定のオブジェクトやコレクション全体、あるいはクエリの変更を \"監視\" することができます。\n\nウォッチャーを使うと、データベースの変更に迅速に対応することができます。例えば、連絡先が追加されたときに UI を再構築したり、ドキュメントが更新されたときにネットワークリクエストを送ったりすることができます。\n\nウォッチャーは、トランザクションが正常にコミットされ、ターゲットが実際に変更された後に通知されます。\n\n## オブジェクトの監視\n\n特定のオブジェクトが作成、更新、削除されたときに通知を受けたい場合、そのオブジェクトを監視する必要があります：\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\n上記の例からわかるように、オブジェクトはまだ存在しなくてもかまいません。オブジェクトが作成されると、ウォッチャーに通知されます。\n\n追加のパラメータとして `fireImmediately` があります。これを `true` に設定すると、Isar はオブジェクトの現在の値を即座に Stream に追加します。\n\n### レイジーウォッチング\n\n新しい値を受け取る必要はなく、変更された事についてのみ通知して欲しい場合があるかもしれません。\nその場合、Isarはオブジェクトを取得する手間を省くことができます。\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## コレクションの監視\n\n単一のオブジェクトを監視する代わりに、コレクション全体を監視し、いずれかのオブジェクトが追加、更新、または削除されたときに通知を受けることができます：\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## クエリの監視\n\nクエリ全体を監視することも可能です。Isarは、クエリの結果が実際に変更されたときのみ通知するよう最善を尽くします。ただ、リンクが原因でクエリが変更された場合は通知されません。リンクの変更について通知を受ける必要がある場合は、コレクションウォッチャーを使用してください。\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\noffset & limit や distinct クエリを使用する場合, オブジェクトがフィルタにマッチしたが、クエリの外でoffset & limitなどから結果が変化した場合にも、Isar は通知します。\n:::\n\n`watchObject()` と同様に、`watchLazy()` を使うと、クエリの結果が変わっても、結果を取得せずに通知を受けることができます。\n\n:::danger\n変更があるたびにクエリを再実行するのは非効率です。その代わりにLazyコレクションウォッチャーを使うとよいでしょう。\n:::\n"
  },
  {
    "path": "docs/docs/ko/README.md",
    "content": "---\nhome: true\ntitle: 홈\nheroImage: /isar.svg\nactions:\n  - text: 시작하기!\n    link: /tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 플러터를 위해 만들었어요\n    details: 설정은 최소로, 사용하기 쉽고, 구성도 없고, 보일러 플레이트도 없습니다. 코드 몇 줄만 추가하면 바로 시작할 수 있습니다.\n  - title: 🚀 뛰어난 확장성\n    details: 수십만 개의 레코드를 단일 NoSQL 데이터베이스에 저장하고 효율적이고 비동기적으로 쿼리할 수 있습니다.\n  - title: 🍭 풍부한 기능\n    details: Isar에는 데이터를 관리하기 위한 다양한 기능이 있습니다. 복합 & 다중 항목 인덱스, 쿼리 수정자, JSON 지원 등이 있습니다.\n  - title: 🔎 전체 텍스트 검색\n    details: Isar는 전체 텍스트 검색 기능이 내장되어 있습니다. 다중 항목 색인을 작성하고 레코드를 쉽게 검색할 수 있습니다.\n  - title: 🧪 ACID 시멘틱\n    details: Isar는 ACID를 준수하며 트랜잭션을 자동으로 처리합니다. 오류가 발생하면 변경 내용을 롤백합니다.\n  - title: 💃 정적 타입\n    details: Isar의 쿼리는 정적 타입이고, 컴파일 시간에 검사됩니다. 런타임 오류에 대해 걱정할 필요가 없습니다.\n  - title: 📱 다중 플랫폼\n    details: iOS, Android, Desktop 및 완전한 웹 지원!\n  - title: ⏱ 비동기\n    details: 병렬 쿼리 작업 및 다중 Isolate 지원을 즉시 사용할 수 있습니다.\n  - title: 🦄 오픈 소스입니다.\n    details: 모든 것이 오픈 소스이며 영원히 무료입니다.\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/ko/crud.md",
    "content": "---\ntitle: CRUD 조작\n---\n\n# CRUD 조작\n\n컬렉션이 정의되었다면, 이제 조작하는 방법을 배워봅시다!\n\n## Isar 열기\n\n무엇을 하든 우선 Isar 인스턴스가 필요합니다. 각 인스턴스에는 데이터베이스 파일을 저장할 수 있도록 쓰기 권한이 있는 디렉토리가 필요합니다. 디렉토리를 지정하지 않는 경우 Isar는 현재 플랫폼에 적합한 기본 디렉토리를 찾습니다.\n\nIsar 인스턴스에서 사용하고 싶은 모든 스키마를 지정합니다. 여러 인스턴스를 열고 있는 경우에도 각각의 인스턴스에 동일한 스키마를 부여해야 합니다.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [RecipeSchema],\n  directory: dir.path,\n);\n```\n\n기본 구성을 사용하거나 다음 매개 변수 중 일부를 제공할 수 있습니다:\n\n| Config              | Description                                                                                                                                                                                                                                |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `name`              | 여러 인스턴스를 다른 이름으로 엽니다. 기본적으로 `\"default\"`가 사용됩니다.                                                                                                                                                                 |\n| `directory`         | 이 인스턴스의 저장 장소 입니다. 상대 경로 또는 절대 경로를 전달할 수 있습니다. 기본값으로, iOS 에서는 `NSDocumentDirectory`, Android 에서는 `getDataDirectory` 가 사용됩니다. 웹에서는 필요하지 않습니다.                                  |\n| `maxSizeMib`        | 데이터베이스 파일의 최대 크기(MiB)입니다. Isar는 무한하지 않은 가상 메모리를 사용하므로 여기 값을 명심하세요. 여러 인스턴스를 열면 사용 가능한 가상 메모리가 공유되므로 각 인스턴스의 `maxSizeMib` 가 더 작아집니다. 기본값은 2048 입니다. |\n| `relaxedDurability` | 내구성 보장을 완화하여 쓰기 성능을 향상 시킵니다. 시스템 충돌(앱 충돌이 아닌) 의 경우 마지막으로 커밋된 트랜잭션이 손실될 수 있습니다. 완전히 파손(Corruption)될 가능성은 없습니다.                                                        |\n| `compactOnLaunch`   | 인스턴스를 열 때 데이터베이스를 압축해야 하는지 여부를 확인하는 조건입니다.                                                                                                                                                                |\n| `inspector`         | 디버그 빌드에 대해서 Inspector 를 사용하도록 설정합니다. 프로파일이나 릴리즈 빌드에서는 이 옵션이 무시됩니다.                                                                                                                              |\n\n인스턴스가 이미 열려 있는 경우 `Isar.open()` 을 호출하면 지정된 매개 변수에 관계없이 기존 인스턴스가 생성됩니다. isolate 안에서 Isar 를 사용할 때 유용합니다.\n\n:::tip\n모든 플랫폼에서 유효한 저장 경로를 얻기 위해서 [path_provider](https://pub.dev/packages/path_provider) 패키지를 사용하는 것을 고려해보세요.\n:::\n\n데이터베이스 파일의 저장 위치는 `directory/name.isar` 입니다.\n\n## 데이터베이스에서 읽기\n\nIsar 에서 지정된 타입의 새로운 객체를 찾고 쿼리하고 생성할 때 `IsarCollection` 인스턴스를 사용합니다.\n\n밑에 나올 예시들에서, 우리는 다음과 같이 정의된 `Recipe` 컬렉션이 있다고 가정합니다.\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### 컬렉션을 가져오기\n\n모든 컬렉션들은 Isar 인스턴스 안에 있습니다. 레시피 컬렉션은 다음 방법으로 가져옵니다:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\n너무 쉽죠! 컬렉션 접근자를 사용하기 싫다면, `collection()` 메서드를 사용해도 됩니다.\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### 객체 얻기 (id를 이용)\n\n아직 컬렉션에 데이터가 들어있지 않지만, 아이디 `123` 의 가상의 객체가 있다고 가정하고 가져오겠습니다.\n\n```dart\nfinal recipe = await isar.recipes.get(123);\n```\n\n`get()` 은 객체를 `Future` 로 반환하고, 해당 객체가 존재하지 않는 경우에는 `null` 을 반환합니다. 모든 Isar 작업들은 기본적으로 비동기적으로 작동합니다. 대부분의 경우는 동기적인 방법도 가지고 있습니다.\n\n```dart\nfinal recipe = isar.recipes.getSync(123);\n```\n\n:::warning\nUI isolate 에서는 비동기 버전을 기본적으로 사용해야 합니다. 하지만 Isar 는 매우 빠르기 때문에, 동기식으로 사용하는 것도 종종 허용됩니다.\n:::\n\n한 번에 여러 객체를 가져오려면 `getAll()` 또는 `getAllSync()` 를 사용하세요:\n\n```dart\nfinal recipe = await isar.recipes.getAll([1, 2]);\n```\n\n### 객체 쿼리\n\nid를 이용해서 객체를 가져오는 대신, `.where()` 과 `.filter()` 를 사용해서 특정 조건에 맞는 객체 목록을 쿼리할 수 있습니다:\n\n```dart\nfinal allRecipes = await isar.recipes.where().findAll();\n\nfinal favouires = await isar.recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ 더 알아보기: [Queries](queries)\n\n## 데이터베이스 수정하기\n\n드디어 컬렉션을 수정할 때가 됐습니다! 객체를 생성, 갱신, 삭제하려면 쓰기 트랜잭션 안에서 각각의 작업들을 수행하세요.\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await isar.recipes.get(123)\n\n  recipe.isFavorite = false;\n  await isar.recipes.put(recipe); // 갱신 작업을 수행합니다.\n\n  await isar.recipes.delete(123); // 또는 삭제 작업\n});\n```\n\n➡️ 더 알아보기: [Transactions](transactions)\n\n### 객체 삽입\n\nIsar 에 객체를 보존하기 위해서, 컬렉션에 집어넣어야 합니다. 컬렉션에 객체를 삽입할 때는 Isar의 `put()` 메소드를 이용합니다. 만약 이미 들어있는 객체라면 갱신을 합니다.\n\nid 필드가 `null` 이나 `Isar.autoIncrement` 라면, Isar 는 자동 증분 아이디를 사용합니다.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await isar.recipes.put(pancakes);\n})\n```\n\n`id` 필드가 final 이 아닌 경우에 Isar 가 id를 객체에 자동으로 할당합니다.\n\n여러 객체를 한 번에 삽입하는 것도 쉽습니다:\n\n```dart\nawait isar.writeTxn(() async {\n  await isar.recipes.putAll([pancakes, pizza]);\n})\n```\n\n### 객체 갱신\n\n`collection.put(object)` 를 이용해서 만들고 갱신하는 동작을 모두 할 수 있습니다. id가 `null`(또는 존재하지 않는 경우) 이라면, 객체는 추가됩니다. 그 이외의 경우에는 갱신됩니다.\n\n만약 팬케익에 즐겨찾기를 해제하는 경우, 이렇게 할 수 있습니다.\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await isar.recipes.put(pancakes);\n});\n```\n\n### 객체 삭제\n\nIsar 에 있는 것을 없애고 싶나요? `collection.delete(id)` 를 사용하세요. delete 메소드는 주어진 id 를 가진 객체를 찾아서 삭제합니다. id `123` 을 가지는 객체를 삭제하는 예시 입니다:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await isar.recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nget 과 put 과 마찬가지로 delete 에도 여러개를 한꺼번에 삭제하는 방법이 있습니다. 삭제된 객체의 수를 반환합니다.\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\n만약 삭제할 객체의 id 를 모른다면 query 를 사용할 수 있습니다.\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/ko/faq.md",
    "content": "---\ntitle: 자주 묻는 질문들\n---\n\n# 자주 묻는 질문들\n\nIsar 와 Flutter 데이터베이스에 대해서 자주 물어보는 질문들을 랜덤으로 뽑아봤습니다.\n\n### 데이터베이스가 왜 필요하죠?\n\n> 저는 백엔드 데이터베이스에 데이터를 보관해요. 왜 Isar 가 필요하죠?\n\n심지어 요즘에도, 지하철이나 비행기 안에 있거나 와이파이가 없는 할머니 집을 갈 때는 데이터 연결이 없는 경우가 흔하게 있습니다. 나쁜 연결로 인해서 앱이 먹통이 되는 일이 없어야 합니다!\n\n### Isar vs Hive\n\n답은 간단합니다: Isar 는 [Hive의 대체재로 시작](https://github.com/hivedb/hive/issues/246) 했었고 지금은 저는 항상 Hive보다 Isar 를 사용하는 것을 추천합니다.\n\n### Where 절?!\n\n> 왜 **_내_** 가 어떤 인덱스를 사용할 지 선택해야 합니까?\n\n여러 이유가 있습니다. 대부분의 데이터베이스는 휴리스틱을 사용해서 주어진 쿼리에 가장 적합한 인덱스를 선택합니다. 데이터베이스가 추가 사용량 데이터(-> 오버헤드) 를 수집해야 하지만 여전히 잘못된 인덱스를 선택할 수 있습니다. 또한 쿼리를 작성하는 속도가 느려지게 됩니다.\n\n개발자인 여러분보다 당신의 데이터를 잘 아는 사람은 아무도 없습니다. 따라서 최적의 인덱스를 선택하고 쿼리나 정렬에 사용할 인덱스를 결정할 수 있습니다.\n\n### 인덱스 / where 절을 사용해야 합니까?\n\n아뇨! 필터에만 의존해도 Isar 는 충분히 빠릅니다.\n\n### Isar 가 충분히 빠른가요?\n\nIsar는 모바일용 데이터베이스 중 가장 빠르기 때문에 대부분의 사용 사례에서 충분히 빠릅니다. 성능 문제가 발생하면 뭔가를 잘못했을 가능성이 높습니다.\n\n### Isar 가 제 앱의 크기를 늘리나요?\n\n조금은 그렇죠. Isar는 다운로드 크기를 1 - 1.5 MB 정도 늘릴 겁니다. Isar Web 은 몇 KB 만 추가합니다.\n\n### 문서가 잘못됐네요 / 오타가 있어요.\n\n이런, 죄송해요. [이슈 열기](https://github.com/isar-community/isar/issues/new/choose) 또는 PR 을 통해서 고쳐주세요. 💪.\n"
  },
  {
    "path": "docs/docs/ko/indexes.md",
    "content": "---\ntitle: 인덱스\n---\n\n# 인덱스\n\n인덱스는 Isar 의 가장 강력한 기능입니다. 대부분의 내장 데이터베이스는 \"일반적인\" 인덱스만을 제공하지만(인덱스가 있다면요), Isar 에는 복합 및 다중 항목 인덱스도 있습니다. 쿼리 성능을 최적화하려면 인덱스 작동 방식을 이해하는 것이 필수적입니다. Isar 를 사용하면 사용할 인덱스와 인덱스 사용 방법을 선택할 수 있습니다. 인덱스가 무엇인지에 대한 간단한 소개로 시작하겠습니다.\n\n## 인덱스가 뭔가요?\n\n컬렉션이 인덱싱되지 않은 경우, 쿼리 입장에서는 행의 순서가 전혀 최적화 되지 않은 것으로 식별되지 않을 수 있습니다. 그래서 쿼리는 객체를 선형으로 검색해야만 합니다. 즉, 쿼리는 조건과 일치하는 객체를 찾기 위해서 모든 객체를 검색해야 합니다. 예상한대로, 그건 시간이 오래 걸립니다. 모든 객체를 하나하나 훑어보는 것은 그다지 효율적이지 않습니다.\n\n예를 들어 이 `Product` 컬렉션에는 전혀 순서가 없습니다.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n#### 데이터:\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\n가격이 30 유로 이상인 모든 제품을 찾는 쿼리는 9개 행을 모두 검색해야 합니다. 9개 행은 문제가 없지만, 10만 행이 되면 문제가 될 수 있습니다.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\n이 쿼리 성능을 개선하기 위해서 우리는 `price` 속성을 인덱스해야 합니다. 인덱스는 정렬된 룩업 테이블과 같습니다.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n#### 생선된 인덱스:\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\n이제 쿼리는 훨씬 빠르게 실행할 수 있습니다. 실행자(executor) 는 마지막 3 개의 인덱스 행으로 바로 이동해서 ID 로 해당 객체를 찾을 수 있습니다.\n\n### 정렬\n\n또 다른 멋진 점은 인덱스가 매우 빠른 정렬을 할 수 있다는 것입니다. 정렬된 쿼리는 정렬하기 전에 데이터베이스가 모든 결과를 메모리에 로드해야 하므로 비용이 많이 듭니다. 오프셋이나 제한을 지정하더라도 정렬 이후에 적용됩니다.\n\n가장 싼 4개의 제품을 찾고 싶다고 가정해 보겠습니다. 다음 쿼리를 사용할 수 있습니다.\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\n이 예에서 데이터베이스는 모든 (!) 객체를 로드하고 가격별로 정렬한 다음 가장 낮은 가격으로 4개의 제품을 반환해야 합니다.\n\n예상대로, 전의 인덱스를 사용하면 훨씬 효율적으로 작업을 수행할 수 있습니다. 데이터베이스는 인덱스의 처음 4개 행을 사용하고 해당 객체가 이미 올바른 순서에 있으므로 해당 객체를 반환합니다.\n\n정렬에 인덱스를 사용하려면 다음과 같이 쿼리를 작성합니다.\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\n`.anyX()` 여기서 절은 Isar 에 정렬에만 인덱스를 사용하도록 지시합니다. `.priceGreaterThan()` 과 같은 where 절을 사용해서 정렬된 결과를 얻을 수도 있습니다.\n\n## 고유 인덱스(Unique indexes)\n\n고유 인덱스는 인덱스에 중복된 값이 포함되지 않게 합니다. 고유 인덱스는 하나 이상의 속성으로 이루어 집니다. 고유한 인덱스에 속성이 하나 있으면 이 속성의 값이 고유하게 됩니다(중복이 허용되지 않게 됩니다). 고유 인덱스에 둘 이상의 속성이 있는 경우 이러한 속성의 값 조합은 고유합니다.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\n중복을 유발하는 데이터 삽입이나 업데이트를 시도하면 오류가 발생합니다:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> 괜찮습니다.\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// 같은 유저 이름으로 유저 삽입을 시도\nawait isar.users.put(user2); // -> 에러: 고유 제약조건 위반\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## 인덱스 대체 (replace indexes)\n\n고유 제약조건을 위반할 경우에 에러가 발생하는 것이 좋지 않을 수도 있습니다. 대신에 기존 객체를 새로운 객체로 대체할 수 있습니다. 이는 인덱스의 `replace` 속성을 `true` 로 설정해서 수행할 수 있습니다.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\n이제 기존 사용자 이름을 가진 사용자를 삽입하려고 하면 Isar 가 기존 사용자를 새 사용자로 대체합니다.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\n인덱스 대체는 객체를 바꾸는 대신 업데이트할 수 있는 `putBy()` 메서드를 생성합니다. 기존 ID 는 재사용되고 링크는 여전히 채워집니다.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// user does not exist so this is the same as put()\nawait isar.users.putByUsername(user1);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nAs you can see, the id of the first inserted user is reused.\n\n## Case-insensitive indexes\n\nAll indexes on `String` and `List<String>` properties are case-sensitive by default. If you want to create a case-insensitive index, you can use the `caseSensitive` option:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## 인덱스 유형\n\nThere are different types of indexes. Most of the time, you'll want to use an `IndexType.value` index, but hash indexes are more efficient.\n\n### Value index\n\nValue indexes are the default type and the only one allowed for all properties that don't hold Strings or Lists. Property values are used to build the index. In the case of lists, the elements of the list are used. It is the most flexible but also space-consuming of the three index types.\n\n:::tip\nUse `IndexType.value` for primitives, Strings where you need `startsWith()` where clauses, and Lists if you want to search for individual elements.\n:::\n\n### Hash index\n\nStrings and Lists can be hashed to reduce the storage required by the index significantly. The disadvantage of hash indexes is that they can't be used for prefix scans (`startsWith` where clauses).\n\n:::tip\nUse `IndexType.hash` for Strings and Lists if you don't need `startsWith`, and `elementEqualTo` where clauses.\n:::\n\n### HashElements index\n\nString lists can be hashed as a whole (using `IndexType.hash`), or the elements of the list can be hashed separately (using `IndexType.hashElements`), effectively creating a multi-entry index with hashed elements.\n\n:::tip\nUse `IndexType.hashElements` for `List<String>` where you need `elementEqualTo` where clauses.\n:::\n\n## Composite indexes\n\nA composite index is an index on multiple properties. Isar allows you to create composite indexes of up to three properties.\n\nComposite indexes are also known as multiple-column indexes.\n\nIt's probably best to start with an example. We create a person collection and define a composite index on the age and name properties:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n#### Data:\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n#### Generated index\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nThe generated composite index contains all persons sorted by their age their name.\n\nComposite indexes are great if you want to create efficient queries sorted by multiple properties. They also enable advanced where clauses with multiple properties:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nThe last property of a composite index also supports conditions like `startsWith()` or `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Multi-entry indexes\n\nIf you index a list using `IndexType.value`, Isar will automatically create a multi-entry index, and each item in the list is indexed toward the object. It works for all types of lists.\n\nPractical applications for multi-entry indexes include indexing a list of tags or creating a full-text index.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` splits a string into words according to the [Unicode Annex #29](https://unicode.org/reports/tr29/) specification, so it works for almost all languages correctly.\n\n#### Data:\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nEntries with duplicate words only appear once in the index.\n\n#### Generated index\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nThis index can now be used for prefix (or equality) where clauses of the individual words of the description.\n\n:::tip\nInstead of storing the words directly, also consider using the result of a [phonetic algorithm](https://en.wikipedia.org/wiki/Phonetic_algorithm) like [Soundex](https://en.wikipedia.org/wiki/Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/ko/limitations.md",
    "content": "# 제한 사항\n\n아시다시피 Isar 는 VM 에서 실행되는 모바일 장치 및 데스크톱과 웹에서 작동합니다. 두 플랫폼은 매우 다르고 다른 한계점을 가지고 있습니다.\nAs you know, Isar works on mobile devices and desktops running on the VM as well as Web. Both platforms are very different and have different limitations.\n\n## VM 에서의 제한사항\n\n- where 절에는 문자열의 처음 1024바이트만 사용할 수 있습니다.\n- 객체의 크기는 16MB 를 넘을 수 없습니다.\n\n## 웹 에서의 제한사항\n\nIsar Web 은 IndexedDB 에 의존하고 있습니다. 그래서 더 많은 제약이 있지만, Isar 를 사용하는 동안 거의 눈치채기 어렵습니다.\n\n- 동기식 메서드들은 지원되지 않습니다.\n- 현재 `Isar.splitWords()` 및 `.matches()` 필터가 구현되지 않았습니다.\n- 스키마 변경 사항이 VM에서만큼 엄격하게 확인되지 않기 때문에 규칙을 준수하도록 주의하십시오.\n- 모든 숫자 유형이 두 배(js의 number 타입) 으로 저장되므로 `@Size32` 가 효과가 없습니다.\n- 해시 인덱스가 더 적은 공간을 사용하지 않도록 인덱스가 다르게 표기됩니다.(여전히 동일하게 작동합니다.)\n- `col.delete()` 및 `col.deleteAll()` 이 올바르게 작동하지만 반환 값은 올바르지 않습니다.\n- `col.clear()` 자동 증분 값을 초기화 하지 않습니다.\n- `NaN` 값으로 지원되지 않습니다.\n"
  },
  {
    "path": "docs/docs/ko/links.md",
    "content": "---\ntitle: 링크\n---\n\n# 링크\n\n링크를 사용해서 댓글 작성자(사용자) 같은 객체 간의 관계를 나타낼 수 있습니다. Isar 링크를 사용해서 `1:1`, `1:n`, 과 `n:n` 관계를 모델링할 수 있습니다. 링크를 사용하는 것은 내장된 객체를 사용하는 것보다 인체 공학적이지 않으므로(less ergonomic), 가능하다면 임베드된 객체를 사용해야 합니다.\n\n링크를 관계를 포함하는 별도의 테이블로 간주합니다. SQL 관계와 비슷하지만 사용가능한 기능과 API 가 다릅니다.\n\n## IsarLink\n\n`IsarLink<T>` 는 관련 객체를 포함하지 않거나 하나만 포함할 수 있으며 일대일 관계를 표현하는데 사용할 수 있습니다. `IsarLink` 에는 연결된 객체를 가지는 `value` 라는 단일 속성이 있습니다.\n\n링크는 게으르므로 `IsarLink` 에 `value` 를 명시적으로 로드하거나 저장하도록 지시해야 합니다. `linkProperty.load()` 및 `linkProperty.save()` 를 호출하여 이 작업을 수행할 수 있습니다.\n\n:::tip\n링크의 원본 및 대상 컬렉션의 ID 타입은 final 이 아니어야 합니다.\n:::\n\n웹이 아닌 타겟에서는, 링크를 처음 사용할 때 링크가 자동 로드 됩니다. 먼저 IsarLink 를 컬렉션에 추가합니다:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\n우리는 선생님과 학생들 사이의 링크를 정의했습니다. 이 예에서 모든 학생은 정확히 한 명의 선생님만 가질 수 있습니다.\n\n먼, 우리는 선생님을 만들어 학생에게 할당합니다. 우리는 선생님을 `.put()` 하고 링크를 수동으로 저장해야 합니다.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teachers.save();\n});\n```\n\n우리는 이제 링크를 이용할 수 있습니다:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\n동기 코드로 똑같이 해보겠습니다. `.putSync()` 는 모든 링크를 자동으로 저장하므로 수동으로 링크를 저장할 필요가 없습니다. 심지어 우리를 위해서 선생님을 만듭니다.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\n이전 예시의 학생이 여러 명의 선생님을 가질 수 있다면 더 그럴듯할 것입니다. 다행히 Isar는 `IsarLinks<T>` 를 가지고 있습니다. 여러 개의 관련 객체를 포함할 수 있으며 -N 관계(1:N, N:N)를 표현할 수 있습니다.\n\n`IsarLinks<T>` 는 `Set<T>` 을 확장하고, set에서 사용하는 모든 메서드들을 사용할 수 있습니다.\n\n`IsarLinks` 는 `IsarLink` 와 비슷한 행동을 하고 lazy 합니다. 연결된 모든 객체를 로드하려면 `linkProperty.load()`를 호출하세요. 변경 내용을 유지하려면, `linkProperty.save()` 를 호출하세요.\n\n내부적으로 `IsarLink` 와 `IsarLinks` 는 동일한 방식으로 표현됩니다. 우리는 이전 예제의 `IsarLink<Teacher>` 를 `IsarLinks<Teacher>` 로 업그레이드 해서 한 학생에 여러 선생님들을 할당할 수 있습니다.\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLinks<Teacher>();\n}\n```\n\n우리가 링크(`teacher`)의 이름을 바꾸지 않았기 때문에 Isar 는 이전의 것들을 기억하고 있습니다.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## 백링크 (Backlinks)\n\n이렇게 물을 수 있습니다. \"만약 역관계를 표현하려면 어떻게 해야 하나요?\". 걱정마세요; 백링크가 있습니다.\n\n백링크는 역방향 링크입니다. 각 링크들은 항상 암시적인 백링크를 가지고 있습니다. `IsarLink`, `IsarLinks` 에 `@BackLink()` 어노테이션을 써서 만들 수 있습니다.\n\n백링크는 추가적인 메모리나 자원을 필요로 하지 않습니다; 항상 데이터 손실 없이 자유롭게 추가하고 삭제하고 이름을 바꿀 수 있습니다.\n\n우리는 특정한 선생님이 어떤 학생을 가지고 있는지를 알고 싶습니다, 그래서 백링크를 정의합니다:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\n우리는 백링크가 어느 링크를 가르키는지를 지정해야 합니다. 두 개의 객체 간에도 여러 링크가 있을 수 있습니다.\n\n## 링크들을 초기화하기\n\n`IsarLink` 와 `IsarLinks` 는 매개변수가 없는 생성자를 가지고, 객체가 만들어 질 때 링크 속성을 할당해야 합니다. 링크 속성을 `final` 로 만드는 것이 좋은 습관입니다.\n\n객체를 처음으로 `put()` 할 때, 링크는 소스 컬렉션과 대상 컬렉션으로 초기화 됩니다. 그 이후, `load()` 와 `save()` 같은 메서드를 호출할 수 있습니다. 링크가 생성된 후 바로 변경 사항을 추적하기 시작하므로 링크가 초기화 되기 전에도 관계를 추가하거나 제거할 수 있습니다.\n\n:::danger\n링크를 또 다른 개체로 옮기는 것은 금지되어 있습니다.\n:::\n"
  },
  {
    "path": "docs/docs/ko/queries.md",
    "content": "---\ntitle: 쿼리\n---\n\n# 쿼리\n\n쿼리는 특정 조건들에 맞는 레코드들을 찾는 방법입니다. 예:\n\n- 별표로 표시된 모든 연락처를 찾습니다.\n- 연락처에서 고유한 이름들을 찾습니다.\n- 성이 정의되지 않은 모든 연락처를 삭제합니다.\n\n쿼리는 다트가 아닌 데이터베이스에서 실행되기 때문에 매우 빠릅니다. 인덱스를 똑똑하게 사용하면 쿼리 성능을 더욱 더 향상시킬 수 있습니다. 아래에서는 쿼리를 작성하는 방법과 쿼리를 가능한 한 빨리 작성하는 방법에 대해 알아봅니다.\n\n레코드들을 필터링하는 방법에는 2가지가 있습니다. 필터를 이용하는 방법과 where 절을 이용하는 방법입니다. 먼저 필터 사용법에 대해 알아보겠습니다.\n\n## 필터\n\n필터는 사용하기 쉽고 이해하기 쉽습니다. 속성들의 타입에 따라 다양한 필터 작업이 가능합니다. 필터 작업들은 대부분 알기 쉬운 이름들을 사용합니다.\n\n필터는 필터링할 컬렉션의 모든 객체에 대한 식을 계산해서 작동합니다. 표현식이 `true` 로 결정되면 Isar 는 결과에 객체를 포함합니다. 필터는 결과 순서에 영향을 주지 않습니다.\n\n아래에 나오는 예제들에서는 다음 모델을 사용합니다.\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### 쿼리 조건들\n\n필드의 타입에 따라서, 다른 조건들을 사용할 수 있습니다.\n\n| 조건                     | 설명                                                                                                                   |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------- |\n| `.equalTo(value)`        | 특정 `value` 와 일치하는 값들.                                                                                         |\n| `.between(lower, upper)` | `lower` 와 `upper` 사이에 있는 값들.                                                                                   |\n| `.greaterThan(bound)`    | `bound` 보다 큰 값들.                                                                                                  |\n| `.lessThan(bound)`       | `bound` 보다 작은 값들. 기본적으로 `null` 값이 사용된다. `null` 은 모든 값들 중에 제일 작은 값으로 간주 되기 때문이다. |\n| `.isNull()`              | `null` 인 값들.                                                                                                        |\n| `.isNotNull()`           | `null` 이 아닌 값들.                                                                                                   |\n| `.length()`              | List, String, 링크에 있는 요소의 개수를 기반으로 한 길이 쿼리 필터                                                     |\n\n데이터베이스에 크기가 39, 40, 46, `null` 인 신발 4켤레가 있다고 가정해보자.\n정렬을 따로 하지 않으면, ID별로 정렬된 값이 반환됩니다.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### 논리 연산들\n\n논리 연산자를 이용해서 구문을 합성할 수 있습니다.\n\n| Operator   | Description                                            |\n| ---------- | ------------------------------------------------------ |\n| `.and()`   | 양 쪽의 식이 모두 `true` 인 경우 `true` 로 평가됩니다. |\n| `.or()`    | 한 쪽의 식이라도 `true` 인 경우 `true` 로 평가됩니다.  |\n| `.xor()`   | 정확히 한 쪽의 식이 `true` 라면 `true` 로 평가됩니다.  |\n| `.not()`   | 다음 식이 부정되는 결과를 가져옵니다.                  |\n| `.group()` | 조건을 그룹화하고 평가 순서를 지정할 수 있습니다.      |\n\n만약 크기가 46인 모든 신발들을 원한다면, 다음 쿼리를 사용할 수 있습니다:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\n하나 이상의 조건이 필요하다면, 논리적 **and** `.and()`, 논리적 **or** `.or()`, 논리적 **xor** `.xor()` 을 이용해서 여러 필터들을 조합하세요.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // 선택적으로, 필터들을 논리 and 연산으로 조합합니다.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\n이 쿼리는 다음과 같습니다: `size == 46 && isUnisex == true`.\n\n`group()` 으로 그룹 조건을 사용할 수도 있습니다:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\n이 쿼리는 `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)` 와 같습니다.\n\n하나의 조건이나 그룹을 부정하려면, 논리적 **부정** 인 `.not()` 을 사용합니다:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\n이 쿼리는 `size != 46 && isUnisex != true` 와 같습니다.\n\n### 문자열 조건들\n\n위에 있는 쿼리 조건들 말고도, String 값에서는 좀 더 많은 조건들이 제공됩니다. 정규식과 유사한 와일드카드를 사용하면 검색의 유연성을 높일 수 있습니다.\n\n| 조건                 | 설명                                           |\n| -------------------- | ---------------------------------------------- |\n| `.startsWith(value)` | 주어진 `value` 로 시작하는 문자열 값들.        |\n| `.contains(value)`   | 주어진 `value` 를 포함하는 문자열 값들.        |\n| `.endsWith(value)`   | 주어진 `value` 로 끝나는 문자열 값들.          |\n| `.matches(wildcard)` | 주어진 `wildcard` 패턴과 일치하는 문자열 값들. |\n\n**대소문자 구분**  \n모든 문자열 연산은 추가적인 `caseSensitive` 매개변수를 가지고 있습니다.\n기본값은 `true`.\n\n**와일드 카드:**  \n[와일드카드 문자열 표현식](https://ko.wikipedia.org/wiki/%EC%99%80%EC%9D%BC%EB%93%9C%EC%B9%B4%EB%93%9C_%EB%AC%B8%EC%9E%90) 다음 2개의 특수한 와일드카드 문자를 포함한 문자열 입니다.\n\n- 와일드카드 `*` 는 0개 이상의 어떠한 문자열과 대응됩니다.\n- 와일드카드 `?` 은 어떠한 문자 하나와 대응됩니다.\n  예를 들어, 와일드카드 문자열 `\"d?g\"` 는 `\"dog\"`, `\"dig\"`, `\"dug\"` 와 일치하지만, `\"ding\"`, `\"dg\"`, `\"a dog\"` 와는 일치하지 않습니다.\n\n### 쿼리 수정자 (query modifiers)\n\n경우에 따라서 일부 조건이나 다른 값들을 기준으로 쿼리를 작성해야 할 수도 있습니다. Isar 에는 조건부 쿼리를 작성하기 위한 매우 강력한 도구가 있습니다.\n\n| 수정자                | 설명                                                                                                                            |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `.optional(cond, qb)` | `condition` 이 `true` 인 경우에만 쿼리를 확장합니다. 조건부로 정렬하거나 제한하기 위해서 쿼리의 모든 곳에서 사용할 수 있습니다. |\n| `.anyOf(list, qb)`    | `value` 의 각 값에 대한 쿼리를 확장하고 논리적 **or** 을 사용해서 조건을 결합합니다.                                            |\n| `.allOf(list, qb)`    | `value` 의 각 값에 대한 쿼리를 확장하고 논리적 **and** 를 사용해서 조건을 결합합니다.                                           |\n|                       |\n\n이 예시에서, 선택적 필터를 사용해서 신발을 찾는 메서드를 만듭니다.\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // sizeFilter != null 이 아닐 때만 적용됩니다.\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\n여러 신발 크기 중 하나를 가진 모든 신발을 찾으려면, 일반적인 쿼리를 작성하거나 `anyOf()` 수정자를 사용할 수 있습니다:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\n쿼리 수정자는 동적 쿼리를 작성할 때 특히 유용합니다.\n\n### 리스트\n\n심지어 리스트를 쿼리할 수도 있습니다:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\n리스트의 길이에 대해서 쿼리할 수 있습니다.\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\n다트 코드로 `tweets.where((t) => t.hashtags.isEmpty);` 와 `tweets.where((t) => t.hashtags.length > 5);` 같습니다. 리스트 요소에 대해서 쿼리할 수 있습니다.\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\n다트 코드로 `tweets.where((t) => t.hashtags.contains('flutter'));` 와 같습니다.\n\n### 임베드된 객체들\n\n임베드된 객체는 Isar 의 가장 유용한 기능 중 하나 입니다. 최상위 객체와 동일한 조건을 사용하여 매우 효율적으로 쿼리할 수 있습니다. 다음과 같은 모델이 있다고 가정합시다:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\n`BMW` 라는 브랜드와 `\"Germany\"` 라는 나라를 갖는 모든 차들을 쿼리하고 싶습니다. 다음 쿼리를 사용해서 이 작업을 수행할 수 있습니다.\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\n항상 중첩된 쿼리들을 그룹화하세요. 위의 쿼리가 다음 쿼리보다 효율적입니다. 결과는 같겠지만요:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### 링크(Link)\n\n모델이 [링크와 백링크](links) 를 포함하고 있다면 연결된 객체 또는 연결된 객체 수를 기준으로 쿼리를 필터링 할 수 있습니다.\n\n:::warning\nIsar 는 링크된 객체를 조회해야 하므로 링크 쿼리의 비용은 비쌀 수 있습니다. 대신 임베드된 객체를 사용하는 것을 고려해 보십시오.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\n수학이나 영어 선생님이 있는 모든 학생을 찾습니다:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\n링크 필터는 하나 이상의 연결된 객체가 조건과 일치하면 `true` 로 평가합니다.\n\n선생님이 없는 모든 학생을 찾아봅시다:\n\n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\n또는 이렇게 할 수 있습니다:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Where 절\n\nWhere 절은 매우 강력한 도구이지만, 제대로 이해하는 것은 약간 어렵습니다.\n\nfilter 와 달리 where 절은 쿼리 조건을 검사하기 위해서 스키마에서 정의된 index 들을 사용합니다. 인덱스를 쿼리하는 것이 레코드 각각을 필터링하는 것보다 훨씬 빠릅니다.\n\n➡️ 더 알아보기: [인덱스](indexes)\n\n:::팁\n기본적으로 where 절을 사용해서 레코드를 최대한 줄이고 나머지에 대해 필터링을 수행해야 합니다.\n:::\n\n논리적 **or** 을 사용하여 where 절만 결합할 수 있습니다. 즉, 여러 where 절들의 합집합을 구할 수는 있지만, 여러 where 절들의 교집합을 쿼리할 수 는 없습니다.\n\n신발 컬렉션에 인덱스를 추가합니다:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\n두 개의 인덱스가 있습니다. `size` 의 인덱스를 사용하면 `.sizeEqualTo()` 와 같은 절을 사용할 수 있습니다. `isUnisex` 의 합성 인덱스는 `isUnisexSizeEqualTo()` 와 같은 where 절을 가능하게 합니다. 하지만 인덱스의 접두사를 항상 사용할 수 있기 때문에 `isUnisexEqualTo()` 도 허용됩니다.\n\n우리는 복합 인덱스를 사용해서 46사이즈의 남녀공용 신발을 찾는 이전의 쿼리를 다시 작성할 수 있습니다. 이 쿼리는 이전 쿼리보다 훨씬 빨라집니다:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nwhere 절은 2개의 초능력을 더 가지고 있습니다: \"무료\" 정렬과 초고속 구별(distinct) 작업을 제공합니다.\n\n### where 절과 filter 결합하기\n\n`shoes.filter()` 쿼리가 기억나죠? 그건 사실 `shoes.where().filter()` 의 줄임 표현입니다. 양 쪽의 장점들을 사용하기 위해서 하나의 쿼리 안에서 where 절과 filter 를 결합할 수 있습니다.\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\n필터링할 개체 수를 줄이기 위해서 where 절이 먼저 적용됩니다. 남은 객체들에 필터가 적용됩니다.\nThe where clause is applied first to reduce the number of objects to be filtered. Then the filter is applied to the remaining objects.\n\n## 정렬\n\n`.sortBy()`, `.sortByDesc()`, `.thenBy()` 및 `.thenByDesc()` 메서드를 사용해서 쿼리를 실행할 때 결과를 정렬하는 방법을 정의합니다.\n\n인덱스를 사용하지 않고 모델 이름 기준으로 오름차순, 크기 기준으로 내림차순 정렬된 모든 신발을 찾으려면 이렇게 합니다.\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\n특히 정렬은 오프셋과 제한 이전에 실행되기 때문에, 많은 결과를 정렬하는 것은 비용이 많이 듭니다. 위의 정렬 방법은 인덱스를 사용하지 않습니다. 다행히, 우리는 where 절 정렬을 다시 사용할 수 있고 백만 개의 객체를 정렬하는 경우에도 번개처럼 빠르게 수행할 수 있습니다.\n\n### where 절 정렬\n\n쿼리에 **단일** where 절을 사용하는 경우 결과가 이미 인덱스 기준으로 정렬되어 있습니다. 정말 큰일입니다!\n\n신발의 크기가 `[43, 39, 48, 40, 42, 45]` 이고 `42` 보다 큰 모든 신발을 찾고 크기별로 정렬한다고 가정해 보겠습니다.\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // 크기 기준으로 정렬까지 됩니다.\n  .findAll(); // -> [43, 45, 48]\n```\n\n결과는 기본적으로 `size` 인덱스 기준으로 정렬됩니다. where 절의 정렬 순서를 반대로 하려면 `sort` 를 `Sort.desc` 로 설정하면 됩니다:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\n가끔 where 절을 이용하지 않지만 암시적인 정렬을 원하는 경우가 있습니다. `any` where 절을 사용하면 됩니다.\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\n복합 인덱스를 사용하는 경우, 인덱스의 모든 필드 별로 결과가 정렬됩니다.\n\n:::tip \n결과를 정렬해야 하는 경우 인덱스를 사용하는 게 좋습니다. 특히 `offset()` 과 `limit()` 를 사용하여 작업하는 경우에는 더욱 그렇습니다.\n::: \n\n인덱스를 사용해서 정렬할 수 없거나 유용하지 않은 경우가 있습니다. 이러한 경우 인덱스를 사용하여 결과 항목 수를 최대한 줄여야 합니다.\n\n## 고유한 값들 (Unique values)\n\n고유한 값들로만 이루어진 항목들을 반환하려면 distinct 술어를 사용하세요. 예를 들어, Isar 데이터베이스에 있는 신발 모델의 수를 확인하려면 다음과 같이 하세요.\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\n여러 개의 개별 조건들을 체인으로 연결해서 모델 크기 조합이 다른 모든 신발을 찾을 수 있습니다.\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\n각 고유한 조합의 첫 번째 결과만 반환됩니다. where 절 및 정렬 작업을 사용하여 이를 제어할 수 있습니다.\n\n### Where 절 구분 (Where clause distinct)\n\n고유하지 않은 인덱스가 있는 경우, 구분된 값들을 모두 가져올 수 있습니다. 이전 섹션의 `distinctBy` 연산을 사용할 수 있지만, 정렬 및 필터 이후에 실행되므로 오버헤드가 있습니다. 단일 where 절만 사용하는 경우 인덱스를 사용하여 구분 작업을 수행할 수 있습니다.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\n이론적으로는 정렬 및 구분을 위해서 여러 개의 where 절을 사용하 수 있습니다. 유일한 제약은 where 절이 중복되지 않고 동일한 인덱스를 사용하는 것입니다. 올바른 정렬을 위해서는 정렬 순서로 적용해야 합니다. 이것에 의존하는 것은 매우 조심하세요!\nIn theory, you could even use multiple where clauses for sorting and distinct. The only restriction is that those where clauses are not overlapping and use the same index. For correct sorting, they also need to be applied in sort order. Be very careful if you rely on this!\n:::\n\n## 오프셋과 제한(Offset & Limit)\n\nlazy 리스트 뷰를 위해서 쿼리 결과를 제한하는 것이 좋습니다. 다음과 같이 `limit()` 를 설정해서 할 수 있습니다.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\n`offset()` 을 이용해서 쿼리를 페이징할 수 있습니다.\nBy setting an `offset()` you can also paginate the results of your query.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nDart 객체를 인스턴스화하는 것은 보통 쿼리 실행에서 비용이 가장 많이 드는 부분이기 때문에, 필요한 객체만 불러오는 것이 좋습니다.\n\n## 실행 순서\n\nIsar 는 항상 다음 순서로 쿼리들을 실행합니다.\n\n1. 주 또는 보조 인덱스를 순회하면서 객체를 찾습니다. (where 절 적용)\n2. Filter\n3. 정렬\n4. 구분 연산\n5. 오프셋 & 제한\n6. 결과 반환\n\n## 쿼리 연산들\n\n이전 예제들에서 일치하는 모든 객체들을 검색하기 위해서 `.findAll()` 을 사용했습니다. 그러나 더 많은 연산을 사용할 수 있습니다.\n\n| 연산             | 설명                                                                                                                 |\n| ---------------- | -------------------------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | 일치하는 첫 객체 또는 일치하는 것이 없는 경우 `null` 을 반환합니다.                                                  |\n| `.findAll()`     | 일치하는 모든 객체들을 검색합니다.                                                                                   |\n| `.count()`       | 쿼리와 일치하는 객체의 수를 셉니다.                                                                                  |\n| `.deleteFirst()` | 컬렉션에서 일치하는 첫 객체를 제거합니다.                                                                            |\n| `.deleteAll()`   | 컬렉션에서 일치하는 모든 객체를 제거합니다.                                                                          |\n| `.build()`       | 쿼리를 나중에 사용하기 위해 컴파일 합니다. 이렇게 하면 쿼리를 여러 번 실행하는 경우 쿼리를 만드는 비용이 절약됩니다. |\n\n## 속성 쿼리 (Property queries)\n\n단일 속성 값에만 관심이 있는 경우 속성 쿼리를 사용하세요. 일반 쿼리를 만들고 속성을 선택하세요:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\n단일 속성만 이용하면 역직렬화에 걸리는 시간을 절약할 수 있습니다. 속성 쿼리는 임베드된 객체와 리스트에도 사용할 수 있습니다.\n\n## 집계 (Aggregation)\n\nIsar 에서는 속성 쿼리의 값을 집계할 수 있습니다. 다음 집계 연산이 가능합니다.\n\n| 연산         | 설명                                                                        |\n| ------------ | --------------------------------------------------------------------------- |\n| `.min()`     | 최소값 또는 일치하는 것이 없는 경우 `null` 을 반환합니다.                   |\n| `.max()`     | 최대값 또는 일치하는 것이 없는 경우 `null` 을 반환합니다.                   |\n| `.sum()`     | 모든 값들을 더합니다.                                                       |\n| `.average()` | 모든 값들의 평균을 계산합니다. 일치하는 값이 없는 경우 `NaN` 을 반환합니다. |\n\n집계를 사용하는 것이 일치하는 모든 객체를 찾은 다음 집계를 수동으로 하는 것보다 훨씬 빠릅니다.\n\n## 동적 쿼리\n\n:::danger\n이 섹션은 대부분 사용자와는 관련이 없습니다. 반드시 필요한 경우(거의 그럴 일은 없습니다.)가 아니면 동적 쿼리를 사용하지 않는 것이 좋습니다.\n:::\n\n위의 모든 예시에서 QueryBuilder와 생성된 정적 확장 메서드들을 사용했습니다. 동적 쿼리 또는 사용자 지정 쿼리 언어 (Isar Inspector 같은) 를 만들 수 있습니다. 이 경우 `buildQuery()` 메서드를 사용할 수 있습니다.\n\n| 매개변수        | 설명                                                                           |\n| --------------- | ------------------------------------------------------------------------------ |\n| `whereClauses`  | 이 쿼리의 where 절들 입니다.                                                   |\n| `whereDistinct` | where 절이 구분된 값을 반환해야 하는 지 여부입니다. (단일 where 절에만 유효함) |\n| `whereSort`     | where 절의 순회 순서 입니다. (단일 where 절에만 유효함)                        |\n| `filter`        | 결과에 적용할 필터입니다.                                                      |\n| `sortBy`        | 정렬의 기준으로 사용할 속성의 리스트입니다.                                    |\n| `distinctBy`    | 구분할 속성 리스트 입니다.                                                     |\n| `offset`        | 결과의 오프셋 입니다.                                                          |\n| `limit`         | 반환할 결과의 최대 개수입니다.                                                 |\n| `property`      | null이 아닌 경우 이 속성의 값만 반환됩니다.                                    |\n\n동적 쿼리를 만들어 봅시다:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\n다음 쿼리와 동일합니다:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/ko/recipes/data_migration.md",
    "content": "---\ntitle: 데이터 마이그레이션 (Data migration)\n---\n\n# 데이터 마이그레이션\n\nIsar 는 컬렉션, 속성, 인덱스를 추가하거나 삭제하면 데이터베이스 스키마를 자동으로 마이그레이션합니다. 가끔은 데이터도 마이그레이션해야 할 수 있습니다. Isar 는 임의 마이그레이션 제한을 적용하기 때문에 기본 제공 솔루션을 제공하지는 않습니다. 사용자의 요구사항에 맞는 마이그레이션 로직을 쉽게 구현할 수 있습니다.\n\n이 예시에서는 전체 데이터베이스에서 하나의 버전을 이용하려고 합니다. shared preferences 를 사용해서 현재 버전을 저장하고 마이그레이션하려는 버전과 비교합니다. 버전이 일치하지 않으면 데이터를 마이그레이션하고 버전을 업데이트 합니다.\n\n:::tip\n각 컬렉션에 자체 버전을 지정하고 개별적으로 마이그레이션 할 수 있습니다.\n:::\n\n생일 필드가 있는 사용자 컬렉션이 있다고 상상해 보십시오. 우리 앱의 버전 2에서는 나이를 기준으로 사용자를 조회할 수 있는 추가 출생 연도 필드가 필요합니다.\n\n버전 1:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\n버전 2:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\n문제는 기존에 있던 사용자 모델들은 버전 1에서 `birthYear` 가 없었기 때문에 비어있는 `birthYear` 를 가지게 된다는 것입니다. 우리는 `birthYear` 필드를 설정하기 위해서 데이터를 마이그레이션 해야 합니다.\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // 버전이 설정되지 않았거나(새로 설치한 경우), 이미 2인 경우 마이그레이션할 필요가 없습니다.\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // 버전 업데이트\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // 모든 사용자를 한 번에 메모리에 로드하지 않도록 사용자를 페이지 분할합니다.\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // 생년월일 게터를 사용하기 때문에 업데이트할 필요가 없습니다.\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\n많은 데이터를 마이그레이션 해야 하는 경우 UI 스레드에 부담이 가지 않도록 백그라운드 isolate 를 사용하는 것이 좋습니다.\n:::\n"
  },
  {
    "path": "docs/docs/ko/recipes/full_text_search.md",
    "content": "---\ntitle: Full-text search\n---\n\n# Full-text search\n\nFull-text search is a powerful way to search text in the database. You should already be familiar with how [indexes](/indexes) work, but let's go over the basics.\n\nAn index works like a lookup table, allowing the query engine to find records with a given value quickly. For example, if you have a `title` field in your object, you can create an index on that field to make it faster to find objects with a given title.\n\n## Why is full-text search useful?\n\nYou can easily search text using filters. There are various string operations for example `.startsWith()`, `.contains()` and `.matches()`. The problem with filters is that their runtime is `O(n)` where `n` is the number of records in the collection. String operations like `.matches()` are especially expensive.\n\n:::tip\nFull-text search is much faster than filters, but indexes have some limitations. In this recipe, we will explore how to work around these limitations.\n:::\n\n## Basic example\n\nThe idea is always the same: Instead of indexing the whole text, we index the words in the text so we can search for them individually.\n\nLet's create the most basic full-text index:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nWe can now search for messages with specific words in the content:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nThis query is super fast, but there are some problems:\n\n1. We can only search for entire words\n2. We do not consider punctuation\n3. We do not support other whitespace characters\n\n## Splitting text the right way\n\nLet's try to improve the previous example. We could try to develop a complicated regex to fix word splitting, but it will likely be slow and wrong for edge cases.\n\nThe [Unicode Annex #29](https://unicode.org/reports/tr29/) defines how to split text into words correctly for almost all languages. It is quite complicated, but fortunately, Isar does the heavy lifting for us:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## I want more control\n\nEasy peasy! We can change our index also to support prefix matching and case-insensitive matching:\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nBy default, Isar will store the words as hashed values which is fast and space efficient. But hashes can't be used for prefix matching. Using `IndexType.value`, we can change the index to use the words directly instead. It gives us the `.titleWordsAnyStartsWith()` where clause:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## I also need `.endsWith()`\n\nSure thing! We will use a trick to achieve `.endsWith()` matching:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nDon't forget reversing the ending you want to search for:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Stemming algorithms\n\nUnfortunately, indexes do not support `.contains()` matching (this is true for other databases as well). But there are a few alternatives that are worth exploring. The choice highly depends on your use. One example is indexing word stems instead of the whole word.\n\nA stemming algorithm is a process of linguistic normalization in which the variant forms of a word are reduced to a common form:\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\nPopular algorithms are the [Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) and the [Snowball stemming algorithms](https://snowballstem.org/algorithms/).\n\nThere are also more advanced forms like [lemmatization](https://en.wikipedia.org/wiki/Lemmatisation).\n\n## Phonetic algorithms\n\nA [phonetic algorithm](https://en.wikipedia.org/wiki/Phonetic_algorithm) is an algorithm for indexing words by their pronunciation. In other words, it allows you to find words that sound similar to the ones you are looking for.\n\n:::warning\nMost phonetic algorithms only support a single language.\n:::\n\n### Soundex\n\n[Soundex](https://en.wikipedia.org/wiki/Soundex) is a phonetic algorithm for indexing names by sound, as pronounced in English. The goal is for homophones to be encoded to the same representation so they can be matched despite minor differences in spelling. It is a straightforward algorithm, and there are multiple improved versions.\n\nUsing this algorithm, both `\"Robert\"` and `\"Rupert\"` return the string `\"R163\"` while `\"Rubin\"` yields `\"R150\"`. `\"Ashcraft\"` and `\"Ashcroft\"` both yield `\"A261\"`.\n\n### Double Metaphone\n\nThe [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) phonetic encoding algorithm is the second generation of this algorithm. It makes several fundamental design improvements over the original Metaphone algorithm.\n\nDouble Metaphone accounts for various irregularities in English of Slavic, Germanic, Celtic, Greek, French, Italian, Spanish, Chinese, and other origins.\n"
  },
  {
    "path": "docs/docs/ko/recipes/multi_isolate.md",
    "content": "---\ntitle: 다중-Isolate 사용법\n---\n\n# 다중-Isolate 사용법\n\n스레드 대신, 모든 다트 코드는 isolate 안에서 돌아갑니다. 각 isolate 에는 고유한 메모리 힙이 있으므로, isolate 의 어떤 상태도 다른 isolate 에서 접근할 수 없습니다.\n\nIsar can be accessed from multiple isolates at the same time, and even watchers work across isolates. In this recipe, we will check out how to use Isar in a multi-isolate environment.\n\n## When to use multiple isolates\n\nIsar transactions are executed in parallel even if they run in the same isolate. In some cases, it is still beneficial to access Isar from multiple isolates.\n\nThe reason is that Isar spends quite some time encoding and decoding data from and to Dart objects. You can think of it as encoding and decoding JSON (just more efficient). These operations run inside the isolate from which the data is accessed and naturally block other code in the isolate. In other words: Isar performs some of the work in your Dart isolate.\n\nIf you only need to read or write a few hundred objects at once, doing it in the UI isolate is not a problem. But for huge transactions or if the UI thread is already busy, you should consider using a separate isolate.\n\n## Example\n\nThe first thing we need to do is to open Isar in the new isolate. Since the instance of Isar is already open in the main isolate, `Isar.open()` will return the same instance.\n\n:::warning\nMake sure to provide the same schemas as in the main isolate. Otherwise, you will get an error.\n:::\n\n`compute()` starts a new isolate in Flutter and runs the given function in it.\n\n```dart\nvoid main() {\n  // Open Isar in the UI isolate\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // listen to changes in the database\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // start a new isolate and create 10000 messages\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // after some time:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// function that will be executed in the new isolate\nFuture createDummyMessages(int count) async {\n  // we don't need the path here because the instance is already open\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // we use a synchronous transactions in isolates\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nThere are a few interesting things to note in the example above:\n\n- `isar.messages.watchLazy()` is called in the UI isolate and is notified of changes from another isolate.\n- Instances are referenced by name. The default name is `default`, but in this example, we set it to `myInstance`.\n- We used a synchronous transaction to create the mesasges. Blocking our new isolate is no problem, and synchronous transactions are a little faster.\n"
  },
  {
    "path": "docs/docs/ko/recipes/string_ids.md",
    "content": "---\ntitle: String ids\n---\n\n# String ids\n\nThis is one of the most frequent requests I get, so here is a tutorial on using String ids.\n\nIsar does not natively support String ids, and there is a good reason for it: integer ids are much more efficient and faster. Especially for links, the overhead of a String id is too significant.\n\nI understand that sometimes you have to store external data that uses UUIDs or other non-integer ids. I recommend storing the String id as a property in your object and using a fast hash implementation to generate a 64-bit int that can be used as Id.\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nWith this approach, you get the best of both worlds: Efficient integer ids for links and the ability to use String ids.\n\n## Fast hash function\n\nIdeally, your hash function should have high quality (you don't want collisions) and be fast. I recommend using the following implementation:\n\n```dart\n/// FNV-1a 64bit hash algorithm optimized for Dart Strings\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nIf you choose a different hash function, ensure it returns a 64-bit int and avoid using a cryptographic hash function because they are much slower.\n\n:::warning\nAvoid using `string.hashCode` because it is not guaranteed to be stable across different platforms and versions of Dart.\n:::\n"
  },
  {
    "path": "docs/docs/ko/schema.md",
    "content": "---\ntitle: 스키마\n---\n\n# 스키마\n\n앱의 데이터를 저장하기 위해 Isar를 사용할 때마다, 컬렉션을 이용하게 됩니다. 컬렉션은 연관된 Isar 데이터베이스의 데이터베이스 테이블과 같고, 하나의 다트 객체 타입만을 포함할 수 있습니다. 각 컬렉션 객체는 해당 컬렉션의 데이터 행을 나타냅니다.\n\n컬렉션의 정의를 \"스키마\" 라고 합니다. Isar Generator 가 힘든 일을 대신 해주고, 컬렉션을 사용하기 위해 필요한 대부분의 코드를 생성해줍니다.\n\n## 컬렉션의 구조\n\n클래스에 `@collection` 또는 `@Collection()` 어노테이션을 붙여서 Isar 컬렉션을 정의합니다. 필드들을 포함하는 하나의 Isar 컬렉션은 데이터베이스의 해당하는 테이블에 있는 각각의 열과 같으며, 여기에는 기본 키를 구성하는 하나의 필드도 포함됩니다.\n\n아래 코드는 `User` 테이블을 정의하는 하나의 컬렉션 예시를 보여줍니다. 테이블에는 ID, 성, 이름에 해당하는 열이 포합됩니다.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\n필드를 저장하기 위해서 Isar 가 반드시 필드에 접근해야 합니다. 필드를 public 으로 만들거나 게터와 세터를 제공해서 Isar가 접근할 수 있도록 만들어야 됩니다.\n:::\n\n컬렉션을 커스터마이징할 수 있는 몇 가지 선택적인 매개변수들이 있습니다.\n\n| Config        | Description                                                                                                      |\n| ------------- | ---------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Isar 에 부모 클래스들과 믹스인에 있는 필드를 저장할 지를 조정합니다. 기본적으로 활성화되어 있습니다. |\n| `accessor`    | 기본 컬렉션 접근자의 이름을 바꿀 수 있게 해줍니다. (예: `Contact` 컬렉션에 사용되는 `isar.contacts`) |\n| `ignore`      | 특정 속성을 제외할 수 있습니다. 이것은 상위 클래스에도 동일하게 적용될 수 있습니다. |\n\n### Isar 의 Id\n\n각 컬렉션 클래스는 객체를 고유하게 식별하는 `Id` 타입으로 id 속성을 정의해야 합니다.\n`Id` 는 Isar Generator 가 id 속성을 인식할 수 있도록 하는 int 의 별칭일 뿐입니다.\n\nIsar 는 id 필드를 자동으로 인덱싱합니다. id 를 기반으로 객체를 효율적으로 가져오고 수정할 수 있습니다.\n\n사용자가 직접 id를 설정하거나 Isar 에 자동 증분 id를 할당하도록 요청할 수 있습니다. 만약 `id` 필드가 `null` 이고 `final` 이 아니라면 Isar 는 자동 증분 id 를 할당합니다. null 이 아닌 자동 증분 id를 원하는 경우에는 `Isar.autoincrement` 를 사용할 수 있습니다.\n\n::tip\n자동 증분 아이디들은 해당 객체가 삭제되어도 다시 사용할 수 없습니다. 자동 증분 id를 초기화하는 유일할 방법은 데이터베이스를 지우는 것 뿐입니다.\n:::\n\n### 컬렉션과 필드의 이름 바꾸기\n\n기본적으로 Isar는 클래스 이름을 컬렉션 이름으로 사용합니다. 마찬가지로 Isar는 필드 이름을 데이터베이스 열 이름으로 사용합니다. 컬렉션이나 필드에 다른 이름을 붙이고 싶다면 `@Name` 어노테이션을 추가합니다. 다음 코드는 컬렉션과 필드의 이름을 바꾸는 예입니다:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\n특히, 이미 데이터베이스에 저장되어 있는 Dart 의 필드나 클래스의 이름을 변경하고 싶다면, `@Name` 어노테이션의 사용을 검토해야 합니다. 그렇지 않으면 데이터베이스가 해당 필드나 컬렉션을 삭제하거나 재작성하게 될 수 있습니다.\n\n### 필드 무시하기\n\nIsar 는 컬렉션 클래스의 모든 public 필드를 저장합니다. 속성이나 게터에 `@ignore` 어노테이션을 붙여서 저장하지 않을 수 있습니다. 다음 코드 조각을 보세요.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\n컬렉션이 부모 컬렉션에서 필드를 상속하는 경우 일반적으로 `@Collection` 어노테이션의 ignore 속성을 사용하는 것이 더 쉽습니다.\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n만약 컬렉션에 Isar가 지원하지 않는 유형의 필드가 포함되어 있다면 해당 필드는 무시해야 합니다.\n\n:::warning\n저장되지 않은 Isar 객체에 정보를 저장하는 것은 좋지 않습니다.\n:::\n\n## 지원하는 타입 목록\n\nIsar 는 아래의 데이터 타입들을 지원합니다:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\n추가적으로 임베드된 객체와 enum 도 지원합니다. 이것들은 아래에서 다룰 것입니다.\n\n## byte, short, float\n\n대부분의 경우 64비트 정수형이나 double 의 전체 범위는 필요하지 않습니다. Isar는 더 작은 수치를 저장할 때를 위해서 용량과 메모리를 절약할 수 있는 추가 유형을 지원합니다.\n\n| Type       | Size in bytes | Range                                                   |\n| ---------- | ------------- | ------------------------------------------------------- |\n| **byte**   | 1             | 0 to 255                                                |\n| **short**  | 4             | -2,147,483,647 to 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 to 3.4e38                                       |\n| **double** | 8             | -1.7e308 to 1.7e308                                     |\n\n추가적인 숫자 타입들은 native 다트 타입들의 별칭일 뿐입니다. 예를 들어 `short` 를 사용해도 `int` 를 사용하는 것과 같이 동작합니다.\n\n아래에 위의 모든 유형을 포함하는 컬렉션의 예를 보여줍니다.\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\n모든 숫자 유형은 리스트로 사용할 수도 있습니다. 바이트들을 저장하려면 `List<byte>` 를 사용해야 합니다.\n\n## 널이 허용되는 타입들\n\nIsar 에서 nullability(DB 테이블의 열 항목이 NULL 값을 가질 수 있는지 없는지)가 어떻게 작동하는지 이해하는 것은 필수입니다. 숫자 타입들은 `null` 이라는 값을 별도로 가지지 **않습니다**. 대신, 특수한 값이 사용됩니다.\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`, `String`, `List` 는 별도의 `null` 표현을 사용합니다.\n\n이런 동작을 통해서 성능이 향상되고, `null` 값을 처리하기 위한 마이그레이션이나 특별한 코드 없이도 필드의 nullability 를 자유롭게 변경할 수 있게 됩니다.\n\n:::warning\n`byte` 타입은 널 값을 지원하지 않습니다.\n:::\n\n## DateTime\n\nIsar 는 날짜의 표준 시간대 정보를 저장하지 않습니다. 대신 `DateTime`을 UTC로 변환해서 저장합니다. Isar 는 모든 날짜를 현지 시간으로 반환합니다.\n\n`DateTime`은 마이크로초 단위로 저장됩니다. 브라우저에서는 자바스크립트의 제한 때문에 밀리초 단위의 정밀도만 지원됩니다.\n\n## Enum\n\nIsar는 다른 Isar 타입들 처럼 열거형을 저장하고 사용할 수 있습니다. 하지만 Isar가 디스크의 열거형을 어떻게 나타낼지 선택해야 합니다. Isar 는 4가지의 전략을 지원합니다.\n\n| EnumType    | Description                                                                                         |\n| ----------- | --------------------------------------------------------------------------------------------------- |\n| `ordinal` | 열거형의 인덱스는 `byte` 로 저장됩니다. 이것은 매우 효율적이지만 널이 가능한 열거형에서는 허용하지 않습니다 |\n| `ordinal32` | 열거형의 인덱스는 `short`(4바이트 정수)로 저장됩니다. |\n| `name` | 열거형 이름은 `String` 으로 저장됩니다. |\n| `value` | 사용자 지정 속성을 사용하여 열거형을 검색합니다 |\n\n:::warning\n`ordinal`과 `ordinal32` 는 열거값의 순서에 따라 달라집니다. 순서를 변경하는 경우, 기존 데이터베이스는 잘못된 값을 반환합니다.\n:::\n\n각각의 전략을 사용하는 예시를 확인하세요.\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // EnumType.ordinal 과 같습니다.\n  late TestEnum byteIndex; // 널이 될 수 없습니다.\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // 널이 될 수 없습니다.\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\n물론 열거형을 리스트에서 사용해도 됩니다.\n\n## 임베드된 객체\n\n컬렉션 모델에 중첩된 객체가 있는 것이 도움이 되는 경우가 많습니다. 객체를 중첩할 수 있는 깊이에는 제한이 없습니다. 그러나 깊이 중첩된 객체를 업데이트하려면 전체 객체 트리를 데이터베이스에 기록해야 합니다.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\n임베드된 객체는 null로 사용할 수 없으며 다른 객체를 확장할 수 있습니다. 유일한 요구 사항은 `@embedded` 어노테이션을 추가하고 필수 매개 변수가 없는 기본 생성자를 갖는 것입니다."
  },
  {
    "path": "docs/docs/ko/transactions.md",
    "content": "---\ntitle: 트랜잭션\n---\n\n# 트랜잭션 (Transactions)\n\nIsar 에서 트랜잭션은 단일 작업 단위 안에서 여러 데이터베이스 작업들을 합치게 됩니다. Isar 와의 대부분의 상호작용은 암묵적으로 트랜잭션을 사용합니다. Isar 의 읽기 및 쓰기 접근은 [ACID](http://en.wikipedia.org/wiki/ACID) 를 준수합니다. 오류가 발생하면 트랜잭션은 자동으로 롤백됩니다.\n## 명시적 트랜잭션\n\n명시적 트랜잭션에서는 데이터베이스의 일관된 스냅샷을 얻을 수 있습니다. 트랜잭션 시간을 최소화하려고 노력하세요. 트랜잭션 내부에서 네트워크 호출 및 기타 장기적인 작업들은 금지됩니다.\n\n트랜잭션 (특히 쓰기 트랜잭션) 은 비용이 많이 들기 때문에 항상 연속적인 작업들을 단일 트랜잭션으로 그룹화해야 합니다.\n\n트랜잭션은 동기식이나 비동기식일 수 있습니다. 동기적인 트랜잭션에서는 동기 연산만 수행할 수 있습니다. 비동기식 트랜잭션에서는 비동기 연산만 수행할 수 있습니다.\n\n|        | 읽기         | 읽기 & 쓰기       |\n| ------ | ------------ | ----------------- |\n| 동기   | `.txnSync()` | `.writeTxnSync()` |\n| 비동기 | `.txn()`     | `.writeTxn()`     |\n\n### 읽기 트랜잭션\n\n명시적 읽기 트랜잭션은 선택 사항이지만, 원자적 읽기(atomic reads) 를 수행하고 트랜잭션 내부의 데이터베이스의 일관된 상태에 의존할 수 있게 해줍니다. 내부적으로 Isar 는 모든 읽기 작업에 항상 암시적 읽기 트랜잭션을 사용합니다.\n\n:::tip\n비동기 읽기 트랜잭션은 다른 읽기 및 쓰기 트랜잭션과 병렬로 실행됩니다. 꽤 멋있지 않나요?\n:::\n\n### 쓰기 트랜잭션\n\n읽기 연산과 달리, Isar 에서 쓰기 연산은 반드시 명시적 트랜잭션으로 감싸야 합니다.\n\n쓰기 트랜잭션이 성공적으로 완료되면 자동으로 커밋되고 모든 변경사항이 디스크에 기록됩니다. 오류가 발생하면 트랜잭션이 중단되고 모든 변경 사항이 롤백됩니다. 트랜잭션은 \"전부가 아니면 아무것도 없다(all or nothing)\" 입니다. 트랜잭션 내의 모든 쓰기가 성공하거나, 아니면 데이터 일관성을 보장하기 위해서 모두 무효가 되거나 입니다.\n\n:::warning\n데이터베이스 연산이 실패하면, 그 트랜잭션은 중단되므로 더 이상 사용할 수 없습니다. 다트에서 그 오류를 캐치(catch) 하더라도요.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: 트랜잭션 안으로 for 루프를 이동시키세요.\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/ko/tutorials/quickstart.md",
    "content": "---\ntitle: 빠른 시작\n---\n\n# 빠른 시작\n\n세상에, 이제야 왔군요! 가장 멋진 플러터 데이터베이스를 사용해 보겠습니다...\n\n이 빠른 시작에서는 말은 줄이고 바로 코드를 보겠습니다.\n\n## 1. 의존성 추가하기\n\n재미있는 부분을 보기 전에 `pubspec.yaml` 에 몇 개의 패키지를 추가해야 합니다. 우리는 펍을 이용해서 힘든 일을 쉽게 할 수 있습니다.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. 클래스에 주석 추가(어노테이션)\n\n컬렉션 클래스에 `@collection` 으로 주석을 달고 `Id` 필드를 선택합니다.\n\n```dart\npart 'email.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // id = null 을 사용해도 자동 증분할 수 있습니다.\n\n  String? name;\n\n  int? age;\n}\n```\n\nId는 컬렉션에서 개체를 고유하게 식별하고 나중에 개체를 다시 찾을 수 있도록 합니다.\n\n## 3. 코드 생성기를 실행하기\n\n다음 명령을 실행하여 `build_runner` 를 시작합니다:\n\n```\ndart run build_runner build\n```\n\n플러터를 사용하고 있다면, 다음 명령을 사용합니다.\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Isar 인스턴스 열기\n\n새 Isar 인스턴스를 열고 모든 컬렉션 스키마를 전달합니다. 선택적으로 인스턴스 이름과 디렉토리를 지정할 수도 있습니다.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [EmailSchema],\n  directory: dir.path,\n);\n```\n\n## 5. 읽기와 쓰기\n\n한번 인스턴스를 열면, 콜렉션들을 사용할 수 있습니다.\n\n모든 기본적인 CRUD 작업은 `IsarCollection` 을 통해서 이루어집니다.\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // 삽입 & 업데이트\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // 가져오기\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // 삭제\n});\n```\n\n## 다른 자료들\n\n혹시 영상으로 공부를 하는 것이 더 좋나요? 다음 영상으로 Isar를 시작해보세요:\n\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/ko/watchers.md",
    "content": "---\ntitle: 감시자(Watchers)\n---\n\n# 감시자\n\nIsar 에서는 데이터 변경을 구독할 수 있습니다. 특정 객체, 전체 컬렉션 또는 쿼리의 변경 내용을 \"감시\" 할 수 있습니다.\n\n감시자(Watchers)를 사용하면 데이터베이스의 변경사항에 효율적으로 대응할 수 있습니다. 연락처가 추가될 때 UI를 재구성하거나 문서가 업데이트될 때 네트워크 요청을 보내는 등의 작업을 수행할 수 있습니다.\n\n트랜잭션이 성공적으로 커밋되고 대상이 실제로 변경된 후 감시자에게 통지됩니다.\n\n## 객체 감시\n\n특정 객체가 생성, 업데이트 또는 삭제될 때 알림을 받으려면 객체를 확인해야 합니다:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// 출력: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// 출력: User changed: Mark\n\nawait isar.users.delete(5);\n// 출력: User changed: null\n```\n\n위의 예시에서 볼 수 있듯이 객체가 아직 존재하지 않아도 됩니다. 객체가 생성되면 감시자가 알게 됩니다.\n\n`fireImmediately` 라는 추가 매개 변수가 있습니다. `true` 로 설정하면 Isar 는 즉시 스트림에 객체의 현재 값을 추가합니다.\n\n### 게으른 감시 (Lazy watching)\n\n새 값 말고 변경 사항에 대해서만 구독할 수 있습니다. 그러면 Isar 가 객체를 가져올 필요가 없어집니다.\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// 출력: User 5 changed\n```\n\n## 컬렉션 감시\n\n단일 객체를 감시하는 대신에, 전체 컬렉션을 보고 객체가 추가, 업데이트, 또는 삭제될 때 알아차릴 수 있습니다:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// 출력: A User changed\n```\n\n## 쿼리 감시\n\n전체 쿼리를 감시할 수 있습니다. Isar 는 쿼리 결과가 실제로 변경될 때만 알리려고 최선을 다합니다. 링크로 인해 쿼리가 변경되는 경우 알림이 표시되지 않습니다. 링크 변경에 대한 알림이 필요한 경우 컬렉션 감시를 이용합니다.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// 출력: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// 출력: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// 출력 없음\n\nawait isar.users.put(User()..name = 'Antonia');\n// 출력: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::주의\n오프셋과 제한 또는 별개의 쿼리를 사용하는 경우에, Isar 는 객체가 쿼리의 바깥에 있지만, 필터와 일치하는 경우에도 알림을 받습니다.\n:::\n\n`watchObject()` 처럼 `watchLazy()` 를 사용하여 쿼리 결과가 변경될 때 알림을 받을 수 있지만 결과를 가져오지는 않습니다.\n\n:::위험\n모든 변경 사항에 대해서 쿼리를 다시 실행하는 것은 매우 비효율적입니다. 대신 게으른 컬렉션 감시자를 사용하는 것이 가장 좋습니다.\n:::\n"
  },
  {
    "path": "docs/docs/limitations.md",
    "content": "---\ntitle: Limitations\n---\n\n# Limitations\n\nAs you know, Isar works on mobile devices and desktops running on the VM as well as Web. Both platforms are very different and have different limitations.\n\n## VM Limitations\n\n- Only the first 1024 bytes of a string can be used for a prefix where-clause\n- Objects can only be 16MB in size\n\n## Web Limitations\n\nBecause Isar Web relies on IndexedDB, there are more limitations, but they are barely noticeable while using Isar.\n\n- Synchronous methods are unsupported\n- Currently, `Isar.splitWords()` and `.matches()` filters are not yet implemented\n- Schema changes are not as tightly checked as in the VM so be careful to comply with the rules\n- All number types are stored as double (the only js number type) so `@Size32` has no effect\n- Indexes are represented differently so hash indexes don't use less space (they still work the same)\n- `col.delete()` and `col.deleteAll()` work correctly but the return value is not correct\n- `col.clear()` do not reset the auto-increment value\n- `NaN` is not supported as a value\n"
  },
  {
    "path": "docs/docs/links.md",
    "content": "---\ntitle: Links\n---\n\n# Links\n\nLinks allow you to express relationships between objects, such as a comment's author (User). You can model `1:1`, `1:n`, and `n:n` relationships with Isar links. Using links is less ergonomic than using embedded objects, and you should use embedded objects whenever possible.\n\nThink of the link as a separate table that contains the relation. It's similar to SQL relations but has a different feature set and API.\n\n## IsarLink\n\n`IsarLink<T>` can contain no or one related object, and it can be used to express a to-one relationship. `IsarLink` has a single property called `value` which holds the linked object.\n\nLinks are lazy, so you need to tell the `IsarLink` to load or save the `value` explicitly. You can do this by calling `linkProperty.load()` and `linkProperty.save()`.\n\n:::tip\nThe id property of the source and target collections of a link should be non-final.\n:::\n\nFor non-web targets, links get loaded automatically when you use them for the first time. Let's start by adding an IsarLink to a collection:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nWe defined a link between teachers and students. Every student can have exactly one teacher in this example.\n\nFirst, we create the teacher and assign it to a student. We have to `.put()` the teacher and save the link manually.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\nWe can now use the link:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nLet's try the same thing with synchronous code. We don't need to save the link manually because `.putSync()` automatically saves all links. It even creates the teacher for us.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nIt would make more sense if the student from the previous example could have multiple teachers. Fortunately, Isar has `IsarLinks<T>`, which can contain multiple related objects and express a to-many relationship.\n\n`IsarLinks<T>` extends `Set<T>` and exposes all the methods that are allowed for sets.\n\n`IsarLinks` behaves much like `IsarLink` and is also lazy. To load all linked object call `linkProperty.load()`. To persist the changes, call `linkProperty.save()`.\n\nInternally both `IsarLink` and `IsarLinks` are represented in the same way. We can upgrade the `IsarLink<Teacher>` from before to an `IsarLinks<Teacher>` to assign multiple teachers to a single student (without losing data).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nThis works because we did not change the name of the link (`teacher`), so Isar remembers it from before.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Backlinks\n\nI hear you ask, \"What if we want to express reverse relationships?\". Don't worry; we'll now introduce backlinks.\n\nBacklinks are links in the reverse direction. Each link always has an implicit backlink. You can make it available to your app by annotating an `IsarLink` or `IsarLinks` with `@Backlink()`.\n\nBacklinks do not require additional memory or resources; you can freely add, remove and rename them without losing data.\n\nWe want to know which students a specific teacher has, so we define a backlink:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nWe need to specify the link to which the backlink points. It is possible to have multiple different links between two objects.\n\n## Initialize links\n\n`IsarLink` and `IsarLinks` have a zero-arg constructor, which should be used to assign the link property when the object is created. It is good practice to make link properties `final`.\n\nWhen you `put()` your object for the first time, the link gets initialized with source and target collection, and you can call methods like `load()` and `save()`. A link starts tracking changes immediately after its creation, so you can add and remove relations even before the link is initialized.\n\n:::danger\nIt is illegal to move a link to another object.\n:::\n"
  },
  {
    "path": "docs/docs/pt/README.md",
    "content": "---\nhome: true\ntitle: Home\nheroImage: /isar.svg\nactions:\n  - text: Vamos Começar!\n    link: /pt/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 Feito para Flutter\n    details: Configuração mínima, fácil de usar, sem configuração, sem clichê. Basta adicionar algumas linhas de código para começar.\n  - title: 🚀 Altamente escalável\n    details: Armazene centenas de milhares de registros em um único banco de dados NoSQL e consulte-os de forma eficiente e assíncrona.\n  - title: 🍭 Rico em recursos\n    details: O Isar possui um rico conjunto de recursos para ajudá-lo a gerenciar seus dados. Índices compostos e de várias entradas, modificadores de consulta, suporte a JSON e muito mais.\n  - title: 🔎 Pesquisa de texto completo\n    details: Isar tem busca embutida de texto completo. Crie um índice de várias entradas e pesquise registros facilmente.\n  - title: 🧪 Semântica ACID\n    details: Isar é compatível com ACID e lida com transações automaticamente. Ele reverte as alterações se ocorrer um erro.\n  - title: 💃 Tipagem estática\n    details: As consultas de Isar são estaticamente tipadas e verificadas em tempo de compilação. Não há necessidade de se preocupar com erros de tempo de execução.\n  - title: 📱 Multi plataforma\n    details: iOS, Android, Desktop e SUPORTE COMPLETO DA WEB!\n  - title: ⏱ Assíncrono\n    details: Operações de consulta paralela e suporte multiisolado pronto para uso\n  - title: 🦄 Código Aberto\n    details: Tudo é de código aberto e gratuito para sempre!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/pt/crud.md",
    "content": "---\ntitle: Criar, Ler, Atualizar, Apagar\n---\n\n# Criar, Ler, Atualizar, Apagar\n\nQuando você tiver suas coleções definidas, aprenda a manipulá-las!\n\n## Abrindo Isar\n\nAntes que você possa fazer qualquer coisa, precisamos de uma instância Isar. Cada instância requer um diretório com permissão de gravação onde o arquivo de banco de dados pode ser armazenado. Se você não especificar um diretório, o Isar encontrará um diretório padrão adequado para a plataforma atual.\n\nForneça todos os esquemas que deseja usar com a instância Isar. Se você abrir várias instâncias, ainda precisará fornecer os mesmos esquemas para cada instância.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [RecipeSchema],\n  directory: dir.path,\n);\n```\n\nVocê pode usar a configuração padrão ou fornecer alguns dos seguintes parâmetros:\n\n| Config |  Description |\n| -------| -------------|\n| `name` | Abra várias instâncias com nomes distintos. Por padrão, `\"default\"` em uso. |\n| `directory` | O local de armazenamento para esta instância. Por padrão, `NSDocumentDirectory` é usado para iOS e `getDataDirectory` para Android. Para web não é necessário. |\n| `relaxedDurability` | Diminua a garantia de durabilidade para aumentar o desempenho de gravação. Em caso de falha do sistema (não falha do aplicativo), é possível perder a última transação confirmada. A corrupção não é possível|\n| `compactOnLaunch` | Condições para verificar se o banco de dados deve ser compactado quando a instância for aberta. |\n| `inspector` |Inspetor habilitado para compilações de depuração. Para builds de perfil e versão, esta opção é ignorada. |\n\nSe uma instância já estiver aberta, chamar `Isar.open()` produzirá a instância existente independentemente dos parâmetros especificados. Isso é útil para usar Isar em um isolado.\n\n:::tip\nConsidere usar o pacote [path_provider](https://pub.dev/packages/path_provider) para obter um caminho válido em todas as plataformas.\n:::\n\nO local de armazenamento do arquivo de banco de dados é `directory/name.isar`\n\n## Leitura do banco de dados\n\nUse instâncias `IsarCollection` para localizar, consultar e criar novos objetos de um determinado tipo em Isar.\n\nPara os exemplos abaixo, assumimos que temos uma coleção `Recipe` definida da seguinte forma:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### Obter coleção\n\nTodas as suas coleções residem na instância Isar. Você pode obter a coleção de receitas com:\n\n```dart\nfinal recipes = isar.recipes;\n```\n\nEssa foi fácil! Se você não quiser usar acessadores de coleção, você também pode usar o método `collection()`:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### Obter um objeto (por id)\n\nAinda não temos dados na coleção, mas vamos fingir que temos para que possamos obter um objeto imaginário pelo id `123`\n\n```dart\nfinal recipe = await isar.recipes.get(123);\n```\n\n`get()` retorna um `Future` com o objeto ou `null` se não existir. Todas as operações Isar são assíncronas por padrão e a maioria delas tem uma contrapartida síncrona:\n\n```dart\nfinal recipe = isar.recipes.getSync(123);\n```\n\n:::warning\nVocê deve usar como padrão a versão assíncrona dos métodos em seu isolamento de interface do usuário. Como o Isar é muito rápido, geralmente é aceitável usar a versão síncrona.\n:::\n\nSe você quiser obter vários objetos de uma vez, use `getAll()` ou `getAllSync()`:\n\n```dart\nfinal recipe = await isar.recipes.getAll([1, 2]);\n```\n\n### Objetos de consulta\n\nEm vez de obter objetos por id, você também pode consultar uma lista de objetos que correspondem a certas condições usando `.where()` e `.filter()`:\n\n```dart\nfinal allRecipes = await isar.recipes.where().findAll();\n\nfinal favouires = await isar.recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ Saber mais: [Consultas](queries)\n\n## Modificando o banco de dados\n\nFinalmente chegou a hora de modificar nossa coleção! Para criar, atualizar ou excluir objetos, use as respectivas operações envolvidas em uma transação de gravação:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await isar.recipes.get(123)\n\n  recipe.isFavorite = false;\n  await isar.recipes.put(recipe); // realizar operações de atualização\n\n  await isar.recipes.delete(123); // ou operações de apagar\n});\n```\n\n➡️ Saber mais: [Transações](transactions)\n\n### Inserir objeto\n\nPara persistir um objeto em Isar, insira-o em uma coleção. O método `put()` de Isar irá inserir ou atualizar o objeto dependendo da sua existência na coleção.\n\nSe o campo id for `null` ou `Isar.autoIncrement`, Isar usará um id de incremento automático.\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await isar.recipes.put(pancakes);\n})\n```\n\nIsar atribuirá automaticamente o id ao objeto se o campo `id` não for final.\n\nInserir vários objetos de uma só vez é extremamente fácil:\n\n```dart\nawait isar.writeTxn(() async {\n  await isar.recipes.putAll([pancakes, pizza]);\n})\n```\n\n### Atualizar objeto\n\nTanto a criação quanto a atualização funcionam com `collection.put(object)`. Se o id for `null` (ou não existir), o objeto será inserido; caso contrário, ele é atualizado.\n\nEntão, se quisermos desfavoritar nossas panquecas, podemos fazer o seguinte:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await isar.recipes.put(recipe);\n});\n```\n\n### Apagar objeto\n\nQuer se livrar de um objeto em Isar? Use `collection.delete(id)`. O método delete retorna se um objeto com o id especificado foi encontrado e excluído. Se você quiser excluir o objeto com id `123`, por exemplo, você pode fazer:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await isar.recipes.delete(123);\n  print('Receita apagada: $success');\n});\n```\n\nDa mesma forma para obter e colocar, também há uma operação de exclusão em massa que retorna o número de objetos excluídos:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.deleteAll([1, 2, 3]);\n  print('Apagamos $count receitas');\n});\n```\n\nSe você não souber os ids dos objetos que deseja excluir, poderá usar uma consulta:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('Apagamos $count receitas');\n});\n```\n"
  },
  {
    "path": "docs/docs/pt/faq.md",
    "content": "---\ntitle: QF\n---\n\n# Questões Frequentes\n\nUma coleção aleatória de questões frequentes sobre bancos de dados Isar e Flutter.\n\n### Por que preciso de um banco de dados?\n\n> Armazeno meus dados em um banco de dados backend, por que preciso do Isar?.\n\nAinda hoje, é muito comum não ter conexão de dados se você estiver no metrô ou no avião ou se for visitar sua avó, que não tem WiFi e o sinal do celular é muito ruim. Você não deve deixar uma conexão ruim paralisar seu aplicativo!\n\n### Isar vs Hive\n\nA resposta é fácil: o Isar foi [iniciado como substituto do Hive](https://github.com/hivedb/hive/issues/246) e agora está em um estado em que recomendo sempre usar o Isar sobre o Hive.\n\n### Cláusulas Where?!\n\n> Por que **_I_** precisa escolher qual índice usar?\n\nExistem várias razões. Muitos bancos de dados usam heurísticas para escolher o melhor índice para uma determinada consulta. O banco de dados precisa coletar dados de uso adicionais (-> sobrecarga) e ainda pode escolher o índice errado. Também torna a criação de uma consulta mais lenta.\n\nNinguém conhece seus dados melhor do que você, o desenvolvedor. Assim, você pode escolher o índice ideal e decidir, por exemplo, se deseja usar um índice para consulta ou classificação.\n\n### Eu tenho que usar índices / cláusulas where?\n\nNão! Isar provavelmente é rápido o suficiente se você confiar apenas em filtros.\n\n### Isar é rápido o suficiente?\n\nO Isar está entre os bancos de dados mais rápidos para dispositivos móveis, portanto, deve ser rápido o suficiente para a maioria dos casos de uso. Se você tiver problemas de desempenho, é provável que esteja fazendo algo errado.\n\n### O Isar aumenta o tamanho do meu aplicativo?\n\nUm pouco, sim. O Isar aumentará o tamanho do download do seu aplicativo em cerca de 1 a 1,5 MB. Isar Web adiciona apenas alguns KB.\n\n### Os documentos estão incorretos/há um erro de digitação.\n\nAh não, desculpe. Por favor [abra um issue](https://github.com/isar-community/isar/issues/new/choose) ou, melhor ainda, um PR para corrigi-lo 💪.\n"
  },
  {
    "path": "docs/docs/pt/indexes.md",
    "content": "---\ntitle: Índices\n---\n\n# Índices\n\nOs índices são o recurso mais poderoso do Isar. Muitos bancos de dados incorporados oferecem índices \"normais\" (se houver), mas o Isar também possui índices compostos e de várias entradas. Compreender como os índices funcionam é essencial para otimizar o desempenho da consulta. Isar permite que você escolha qual índice você deseja usar e como deseja usá-lo. Começaremos com uma rápida introdução sobre o que são índices.\n\n## O que são índices?\n\nQuando uma coleção não é indexada, a ordem das linhas provavelmente não será discernível pela consulta como otimizada de forma alguma e, portanto, sua consulta terá que pesquisar os objetos linearmente. Em outras palavras, a consulta terá que pesquisar em todos os objetos para encontrar aqueles que correspondam às condições. Como você pode imaginar, isso pode levar algum tempo. Olhar através de cada objeto não é muito eficiente.\n\nPor exemplo, esta coleção `Product` é totalmente desordenada.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**Dados:**\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nUma consulta que tenta encontrar todos os produtos que custam mais de € 30 deve pesquisar todas as nove linhas. Isso não é um problema para nove linhas, mas pode se tornar um problema para 100 mil linhas.\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nPara melhorar o desempenho desta consulta, indexamos a propriedade `price`. Um índice é como uma tabela de pesquisa classificada:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**Índice gerado:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nAgora, a consulta pode ser executada muito mais rápido. O executor pode pular diretamente para as últimas três linhas do índice e encontrar os objetos correspondentes por seu id.\n\n### Ordenação\n\nOutra coisa legal: os índices podem fazer uma classificação super rápida. As consultas classificadas são caras porque o banco de dados precisa carregar todos os resultados na memória antes de classificá-los. Mesmo se você especificar um deslocamento ou limite, eles serão aplicados após a classificação.\n\nVamos imaginar que queremos encontrar os quatro produtos mais baratos. Poderíamos usar a seguinte consulta:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nNeste exemplo, o banco de dados teria que carregar todos os objetos (!), classificá-los por preço e retornar os quatro produtos com o menor preço.\n\nComo você provavelmente pode imaginar, isso pode ser feito de forma muito mais eficiente com o índice anterior. O banco de dados pega as primeiras quatro linhas do índice e retorna os objetos correspondentes, pois eles já estão na ordem correta.\n\nPara usar o índice para classificação, escreveríamos a consulta assim:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nA cláusula where `.anyX()` diz ao Isar para usar um índice apenas para ordenar. Você também pode usar uma cláusula where como `.priceGreaterThan()` e obter resultados ordenados.\n\n## Índices únicos\n\nUm índice único garante que o índice não contenha valores duplicados. Pode consistir em uma ou várias propriedades. Se um índice único tiver uma propriedade, os valores dessa propriedade serão exclusivos. Se o índice exclusivo tiver mais de uma propriedade, a combinação de valores nessas propriedades será única.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nQualquer tentativa de inserir ou atualizar dados no índice exclusivo que cause uma duplicata resultará em um erro:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// tente inserir usuário com o mesmo username\nawait isar.users.put(user2); // -> error: unique constraint violated\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## Substituir índices\n\nÀs vezes, não é preferível lançar um erro se uma restrição exclusiva for violada. Em vez disso, você pode substituir o objeto existente pelo novo. Isso pode ser feito definindo a propriedade `replace` do índice como `true`.\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nAgora, quando tentamos inserir um usuário com um nome de usuário existente, Isar substituirá o usuário existente pelo novo.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nOs índices de substituição também geram métodos `putBy()` que permitem atualizar objetos em vez de substituí-los. O id existente é reutilizado e os links ainda são preenchidos.\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n//usuário não existe, então é o mesmo que o put()\nawait isar.users.putByUsername(user1); \nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nComo você pode ver, o id do primeiro usuário inserido é reutilizado.\n\n## Índices Case-insensitive\n\nTodos os índices nas propriedades `String` e `List<String>` diferenciam maiúsculas de minúsculas por padrão. Se você deseja criar um índice que não diferencia maiúsculas de minúsculas, pode usar a opção `caseSensitive`:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## Tipo de índice\n\nExistem diferentes tipos de índices. Na maioria das vezes, você desejará usar um índice `IndexType.value`, mas os índices de hash são mais eficientes.\n\n### Índice de valor\n\nÍndices de valor são o tipo padrão e o único permitido para todas as propriedades que não contêm Strings ou Lists. Os valores de propriedade são usados para construir o índice. No caso de listas, os elementos da lista são usados. É o mais flexível, mas também consome espaço dos três tipos de índice.\n\n:::tip\nUse `IndexType.value` para primitivos, Strings onde você precisa de cláusulas `startsWith()` where e Lists se você quiser procurar por elementos individuais.\n:::\n\n### Índice Hash\n\nStrings e Lists podem ser hash para reduzir significativamente o armazenamento exigido pelo índice. A desvantagem dos índices de hash é que eles não podem ser usados para varreduras de prefixo (cláusulas `startsWith` where).\n\n:::tip\nUse `IndexType.hash` para Strings e Lists se você não precisar das cláusulas `startsWith` e `elementEqualTo` where.\n:::\n\n### Índice HashElements\n\nLists de strings podem ser hash como um todo (usando `IndexType.hash`), ou os elementos da list podem ser hash separadamente (usando `IndexType.hashElements`), criando efetivamente um índice de várias entradas com elementos hash.\n\n:::tip\nUse `IndexType.hashElements` para `List<String>` onde você precisa das cláusulas where `elementEqualTo`.\n:::\n\n## Índices compostos\n\nUm índice composto é um índice em várias propriedades. Isar permite criar índices compostos de até três propriedades.\n\nÍndices compostos também são conhecidos como índices de várias colunas.\n\nProvavelmente é melhor começar com um exemplo. Criamos uma coleção de pessoas e definimos um índice composto nas propriedades age e name:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**Dados:**\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**Índice gerado:**\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nO índice composto gerado contém todas as pessoas classificadas por idade e nome.\n\nÍndices compostos são ótimos se você deseja criar consultas eficientes classificadas por várias propriedades. Eles também permitem cláusulas where avançadas com várias propriedades:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nA última propriedade de um índice composto também suporta condições como `startsWith()` ou `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## Índices de várias entradas\n\nSe você indexar uma lista usando `IndexType.value`, o Isar criará automaticamente um índice de várias entradas e cada item da lista será indexado em relação ao objeto. Funciona para todos os tipos de listas.\n\nAs aplicações práticas para índices de várias entradas incluem a indexação de uma lista de tags ou a criação de um índice de texto completo.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` divide uma string em palavras de acordo com a especificação do [Unicode Annex #29](https://unicode.org/reports/tr29/), então funciona para quase todos os idiomas corretamente.\n\n**Dados:**\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nAs entradas com palavras duplicadas aparecem apenas uma vez no índice.\n\n**Índice gerado:**\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nEste índice agora pode ser usado para prefixo (ou igualdade) onde cláusulas das palavras individuais da descrição.\n\n:::tip\nEm vez de armazenar as palavras diretamente, considere também usar o resultado de um [algoritmo fonético](https://en.wikipedia.org/wiki/Phonetic_algorithm) como [Soundex](https://en.wikipedia.org/wiki/ Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/pt/limitations.md",
    "content": "# Limitações\n\nComo você sabe, o Isar funciona em dispositivos móveis e desktops executados na VM e na Web. Ambas as plataformas são muito diferentes e têm limitações diferentes.\n\n## Limitações da VM\n\n- Apenas os primeiros 1024 bytes de uma string podem ser usados ​​para um prefixo where-clause\n- Objetos podem ter apenas 16 MB de tamanho\n\n## Limitações da Web\n\nComo o Isar Web depende do IndexedDB, há mais limitações, mas elas são quase imperceptíveis ao usar o Isar.\n\n- Métodos síncronos não são suportados\n- Atualmente, os filtros `Isar.splitWords()` e `.matches()` ainda não estão implementados\n- As alterações de esquema não são tão rigorosamente verificadas quanto na VM, portanto, tenha cuidado para cumprir as regras\n- Todos os tipos de números são armazenados como double (o único tipo de número js) para que `@Size32` não tenha efeito\n- Os índices são representados de forma diferente para que os índices de hash não usem menos espaço (eles ainda funcionam da mesma forma)\n- `col.delete()` e `col.deleteAll()` funcionam corretamente, mas o valor de retorno não está correto\n- `col.clear()` não redefine o valor de incremento automático\n- `NaN` não é suportado como valor"
  },
  {
    "path": "docs/docs/pt/links.md",
    "content": "---\ntitle: Links\n---\n\n# Links\n\nOs links permitem que você expresse relacionamentos entre objetos, como o autor de um comentário (Usuário). Você pode modelar relacionamentos `1:1`, `1:n` e `n:n` com links Isar. Usar links é menos ergonômico do que usar objetos incorporados e você deve usar objetos incorporados sempre que possível.\n\nPense no link como uma tabela separada que contém a relação. É semelhante às relações SQL, mas possui um conjunto de recursos e API diferentes.\n\n## IsarLink\n\n`IsarLink<T>` pode conter nenhum ou um objeto relacionado e pode ser usado para expressar um relacionamento de um para um. `IsarLink` tem uma única propriedade chamada `value` que contém o objeto vinculado.\nOs links são preguiçosos, então você precisa dizer ao `IsarLink` para carregar ou salvar o `valor` explicitamente. Você pode fazer isso chamando `linkProperty.load()` e `linkProperty.save()`.\n\n:::tip\nA propriedade id das coleções de origem e destino de um link não deve ser final.\n:::\n\nPara destinos não Web, os links são carregados automaticamente quando você os usa pela primeira vez. Vamos começar adicionando um IsarLink a uma coleção:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nDefinimos um vínculo entre teachers e students. Cada student pode ter exatamente um teacher neste exemplo.\n\nPrimeiro, criamos o teacher e o atribuímos a um student. Temos que fazer o `.put()` do teacher e salvar o link manualmente.\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\nAgora podemos usar o link:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nVamos tentar a mesma coisa com código síncrono. Não precisamos salvar o link manualmente porque `.putSync()` salva automaticamente todos os links. Até cria o professor para nós.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nFaria mais sentido se o aluno do exemplo anterior pudesse ter vários professores. Felizmente, Isar tem `IsarLinks<T>`, que pode conter vários objetos relacionados e expressar um relacionamento para muitos.\n\n`IsarLinks<T>` estende `Set<T>` e expõe todos os métodos permitidos para sets.\n\n`IsarLinks` se comporta muito como `IsarLink` e também é preguiçoso. Para carregar todos os objetos vinculados, chame `linkProperty.load()`. Para persistir as alterações, chame `linkProperty.save()`.\n\nInternamente, `IsarLink` e `IsarLinks` são representados da mesma maneira. Podemos atualizar o `IsarLink<Teacher>` de antes para um `IsarLinks<Teacher>` para atribuir vários professores a um único aluno (sem perder dados).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nIsso funciona porque não mudamos o nome do link (`professor`), então Isar se lembra de antes.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## Backlinks\n\nEu ouço você perguntar: \"E se quisermos expressar relacionamentos reversos?\". Não se preocupe; agora vamos apresentar backlinks.\n\nBacklinks são links na direção inversa. Cada link sempre tem um backlink implícito. Você pode disponibilizá-lo para seu aplicativo anotando um `IsarLink` ou `IsarLinks` com `@Backlink()`.\n\nBacklinks não requerem memória ou recursos adicionais; você pode adicionar, remover e renomeá-los livremente sem perder dados.\n\nQueremos saber quais students um teacher específico tem, então definimos um backlink:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nPrecisamos especificar o link para o qual o backlink aponta. É possível ter vários links diferentes entre dois objetos.\n\n## Inicializar links\n\n`IsarLink` e `IsarLinks` possuem um construtor sem argumentos, que deve ser usado para atribuir a propriedade link quando o objeto for criado. É uma boa prática tornar as propriedades do link `final`.\n\nQuando você faz o `put()` do seu objeto pela primeira vez, o link é inicializado com a coleção de origem e destino, e você pode chamar métodos como `load()` e `save()`. Um link começa a rastrear as alterações imediatamente após sua criação, para que você possa adicionar e remover relações antes mesmo de o link ser inicializado.\n\n:::danger\nÉ ilegal mover um link para outro objeto.\n:::\n"
  },
  {
    "path": "docs/docs/pt/queries.md",
    "content": "---\ntitle: Queries\n---\n\n# Queries\n\nQuerying is how you find records that match certain conditions, for example:\n\n- Find all starred contacts\n- Find distinct first names in contacts\n- Delete all contacts that don't have the last name defined\n\nBecause queries are executed on the database and not in Dart, they're really fast. When you cleverly use indexes, you can improve the query performance even further. In the following, you'll learn how to write queries and how you can make them as fast as possible.\n\nThere are two different methods of filtering your records: Filters and where clauses. We'll start by taking a look at how filters work.\n\n## Filters\n\nFilters are easy to use and understand. Depending on the type of your properties, there are different filter operations available most of which have self-explanatory names.\n\nFilters work by evaluating an expression for every object in the collection being filtered. If the expression resolves to `true`, Isar includes the object in the results. Filters do not affect the ordering of the results.\n\nWe'll use the following model for the examples below:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### Query conditions\n\nDepending on the type of field, there are different conditions available.\n\n| Condition | Description |\n| ----------| ------------|\n| `.equalTo(value)` | Matches values that are equal to the specified `value`. |\n| `.between(lower, upper)` | Matches values that are between `lower` and `upper`. |\n| `.greaterThan(bound)` | Matches values that are greater than `bound`. |\n| `.lessThan(bound)` | Matches values that are less than `bound`. `null` values will be included by default because `null` is considered smaller than any other value. |\n| `.isNull()` | Matches values that are `null`.|\n| `.isNotNull()` | Matches values that are not `null`.|\n| `.length()` | List, String and link length queries filter objects based on the number of elements in a list or link. |\n\nLet's assume the database contains four shoes with sizes 39, 40, 46 and one with an un-set (`null`) size. Unless you perform sorting, the values will be returned sorted by id.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### Logical operators\n\nYou can composite predicates using the following logical operators:\n\n| Operator   | Description |\n| ---------- | ----------- |\n| `.and()`   | Evaluates to `true` if both left-hand and right-hand expressions evaluate to `true`. |\n| `.or()`    | Evaluates to `true` if either expression evaluates to `true`. |\n| `.xor()`   | Evaluates to `true` if exactly one expression evaluates to `true`. |\n| `.not()`   | Negates the result of the following expression. |\n| `.group()` | Group conditions and allow to specify order of evaluation. |\n\nIf you want to find all shoes in size 46, you can use the following query:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nIf you want to use more than one condition, you can combine multiple filters using logical **and** `.and()`, logical **or** `.or()` and logical **xor** `.xor()`.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Optional. Filters are implicitly combined with logical and.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nThis query is equivalent to: `size == 46 && isUnisex == true`.\n\nYou can also group conditions using `.group()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nThis query is equivalent to `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nTo negate a condition or group, use logical **not** `.not()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nThis query is equivalent to `size != 46 && isUnisex != true`.\n\n### String conditions\n\nIn addition to the query conditions above, String values offer a few more conditions you can use. Regex-like wildcards, for example, allow more flexibility in search.\n\n| Condition            | Description                                                       |\n| -------------------- | ----------------------------------------------------------------- |\n| `.startsWith(value)` | Matches string values that begins with provided `value`.          |\n| `.contains(value)`   | Matches string values that contain the provided `value`.          |\n| `.endsWith(value)`   | Matches string values that end with the provided `value`.         |\n| `.matches(wildcard)` | Matches string values that match the provided `wildcard` pattern. |\n\n**Case sensitivity**  \nAll string operations have an optional `caseSensitive` parameter that defaults to `true`.\n\n**Wildcards:**  \nA [wildcard string expression](https://en.wikipedia.org/wiki/Wildcard_character) is a string that uses normal characters with two special wildcard characters:\n\n- The `*` wildcard matches zero or more of any character\n- The `?` wildcard matches any character.\n  For example, the wildcard string `\"d?g\"` matches `\"dog\"`, `\"dig\"`, and `\"dug\"`, but not `\"ding\"`, `\"dg\"`, or `\"a dog\"`.\n\n### Query modifiers\n\nSometimes it is necessary to build a query based on some conditions or for different values. Isar has a very powerful tool for building conditional queries:\n\n| Modifier              | Description                                          |\n| --------------------- | ---------------------------------------------------- |\n| `.optional(cond, qb)` | Extends the query only if the `condition` is `true`. This can be used almost anywhere in a query for example to conditionally sort or limit it. |\n| `.anyOf(list, qb)`    | Extends the query for each value in `values` and combines the conditions using logical **or**. |\n| `.allOf(list, qb)`    | Extends the query for each value in `values` and combines the conditions using logical **and**. |\n\nIn this example, we build a method that can find shoes with an optional filter:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // only apply filter if sizeFilter != null\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nIf you want to find all shoes that have one of multiple shoe sizes, you can either write a conventional query or use the `anyOf()` modifier:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nQuery modifiers are especially useful when you want to build dynamic queries.\n\n### Lists\n\nEven lists can be queried:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nYou can query based on the list length:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nThese are equivalent to the Dart code `tweets.where((t) => t.hashtags.isEmpty);` and `tweets.where((t) => t.hashtags.length > 5);`. You can also query based on list elements:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nThis is equivalent to the Dart code `tweets.where((t) => t.hashtags.contains('flutter'));`.\n\n### Embedded objects\n\nEmbedded objects are one of Isar's most useful features. They can be queried very efficiently using the same conditions available for top-level objects. Let's assume we have the following model:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nWe want to query all cars that have a brand with the name `\"BMW\"` and the country `\"Germany\"`. We can do this using the following query:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nAlways try to group nested queries. The above query is more efficient than the following one. Even though the result is the same:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### Links\n\nIf your model contains [links or backlinks](links) you can filter your query based on the linked objects or the number of linked objects.\n\n:::warning\nKeep in mind that link queries can be expensive because Isar needs to look up linked objects. Consider using embedded objects instead.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nWe want to find all students that have a math or English teacher:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nLink filters evaluate to `true` if at least one linked object matches the conditions.\n\nLet's search for all students that have no teachers:\n  \n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\nor alternatively:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Where clauses\n\nWhere clauses are a very powerful tool, but it can be a little challenging to get them right.\n\nIn contrast to filters where clauses use the indexes you defined in the schema to check the query conditions. Querying an index is a lot faster than filtering each record individually.\n\n➡️ Learn more: [Indexes](indexes)\n\n:::tip\nAs a basic rule, you should always try to reduce the records as much as possible using where clauses and do the remaining filtering using filters.\n:::\n\nYou can only combine where clauses using logical **or**. In other words, you can sum multiple where clauses together, but you can't query the intersection of multiple where clauses.\n\nLet's add indexes to the shoe collection:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nThere are two indexes. The index on `size` allows us to use where clauses like `.sizeEqualTo()`. The composite index on `isUnisex` allows where clauses like `isUnisexSizeEqualTo()`. But also `isUnisexEqualTo()` because you can always use any prefix of an index.\n\nWe can now rewrite the query from before that finds unisex shoes in size 46 using the composite index. This query will be a lot faster than the previous one:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nWhere clauses have two more superpowers: They give you \"free\" sorting and a super fast distinct operation.\n\n### Combining where clauses and filters\n\nRemember the `shoes.filter()` queries? It's actually just a shortcut for `shoes.where().filter()`. You can (and should) combine where clauses and filters in the same query to use the benefits of both:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nThe where clause is applied first to reduce the number of objetcs to be filtered. Then the filter is applied to the remaining objetcs.\n\n## Sorting\n\nYou can define how the results should be sorted when executing the query using the `.sortBy()`, `.sortByDesc()`, `.thenBy()` and `.thenByDesc()` methods.\n\nTo find all shoes sorted by model name in ascending order and size in descending order without using an index:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nSorting many results can be expensive, especially since sorting happens before offset and limit. The sorting methods above never make use of indexes. Luckily, we can again use where clause sorting and make our query lightning-fast even if we need to sort a million objects.\n\n### Where clause sorting\n\nIf you use a **single** where clause in your query, the results are already sorted by the index. That's a big deal!\n\nLet's assume we have shoes in sizes `[43, 39, 48, 40, 42, 45]` and we want to find all shoes with a size greater than `42` and also have them sorted by size:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // also sorts the results by size\n  .findAll(); // -> [43, 45, 48]\n```\n\nAs you can see, the result is sorted by the `size` index. If you want to reverse the where clause sort order, you can set `sort` to `Sort.desc`:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nSometimes you don't want to use a where clause but still benefit from the implicit sorting. You can use the `any` where clause:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nIf you use a composite index, the results are sorted by all fields in the index.\n\n:::tip\nIf you need the results to be sorted, consider using an index for that purpose. Especially if you work with `offset()` and `limit()`.\n:::\n\nSometimes it's not possible or useful to use an index for sorting. For such cases, you should use indexes to reduce the number of resulting entries as much as possible.\n\n## Unique values\n\nTo return only entries with unique values, use the distinct predicate. For example, to find out how many different shoe models you have in your Isar database:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nYou can also chain multiple distinct conditions to find all shoes with distinct model-size combinations:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nOnly the first result of each distinct combination is returned. You can use where clauses and sort operations to control it.\n\n### Where clause distinct\n\nIf you have a non-unique index, you may want to get all of its distinct values. You could use the `distinctBy` operation from the previous section, but it's performed after sorting and filters, so there is some overhead.  \nIf you only use a single where clause, you can instead rely on the index to perform the distinct operation.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nIn theory, you could even use multiple where clauses for sorting and distinct. The only restriction is that those where clauses are not overlapping and use the same index. For correct sorting, they also need to be applied in sort order. Be very careful if you rely on this!\n:::\n\n## Offset & Limit\n\nIt's often a good idea to limit the number of results from a query for lazy list views. You can do so by setting a `limit()`:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nBy setting an `offset()` you can also paginate the results of your query.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nSince instantiating Dart objects is often the most expensive part of executing a query, it is a good idea only to load the objects you need.\n\n## Execution order\n\nIsar executes queries always in the same order:\n\n1. Traverse primary or secondary index to find objects (apply where clauses)\n2. Filter objects\n3. Sort results\n4. Apply distinct operation\n5. Offset & limit results\n6. Return results\n\n## Query operations\n\nIn the previous examples, we used `.findAll()` to retrieve all matching objects. There are more operations available, however:\n\n| Operation        | Description                                                                                                         |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | Retreive only the first matching object or `null` if none matches.                                                  |\n| `.findAll()`     | Retreive all matching objects.                                                                                      |\n| `.count()`       | Count how many objects match the query.                                                                             |\n| `.deleteFirst()` | Delete the first matching object from the collection.                                                               |\n| `.deleteAll()`   | Delete all matching objects from the collection.                                                                    |\n| `.build()`       | Compile the query to reuse it later. This saves the cost to build a query if you want to execute it multiple times. |\n\n## Property queries\n\nIf you are only interested in the values of a single property, you can use a property query. Just build a regular query and select a property:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nUsing only a single property saves time during deserialization. Property queries also work for embedded objects and lists.\n\n## Aggregation\n\nIsar supports aggregating the values of a property query. The following aggregation operations are available:\n\n| Operation    | Description                                                    |\n| ------------ | -------------------------------------------------------------- |\n| `.min()`     | Finds the minimum value or `null` if none matches.             |\n| `.max()`     | Finds the maximum value or `null` if none matches.             |\n| `.sum()`     | Sums all values.                                               |\n| `.average()` | Calculates the average of all values or `NaN` if none matches. |\n\nUsing aggregations is vastly faster than finding all matching objects and performing the aggregation manually.\n\n## Dynamic queries\n\n:::danger\nThis section is most likely not relevant to you. It is discouraged to use dynamic queries unless you absolutely need to (and you rarely do).\n:::\n\nAll the examples above used the QueryBuilder and the generated static extension methods. Maybe you want to create dynamic queries or a custom query language (like the Isar Inspector). In that case, you can use the `buildQuery()` method:\n\n| Parameter       | Description                                                                                 |\n| --------------- | ------------------------------------------------------------------------------------------- |\n| `whereClauses`  | The where clauses of the query.                                                             |\n| `whereDistinct` | Whether where clauses should return distinct values (only useful for single where clauses). |\n| `whereSort`     | The traverse order of the where clauses (only useful for single where clauses).             |\n| `filter`        | The filter to apply to the results.                                                         |\n| `sortBy`        | A list of properties to sort by.                                                            |\n| `distinctBy`    | A list of properties to distinct by.                                                        |\n| `offset`        | The offset of the results.                                                                  |\n| `limit`         | The maximum number of results to return.                                                    |\n| `property`      | If non-null, only the values of this property are returned.                                 |\n\nLet's create a dynamic query:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nThe following query is equivalent:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/pt/schema.md",
    "content": "---\ntitle: Schema\n---\n\n# Schema\n\nWhen you use Isar to store your app's data, you're dealing with collections. A collection is like a database table in the associated Isar database and can only contain a single type of Dart object. Each collection object represents a row of data in the corresponding collection.\n\nA collection definition is called \"schema\". The Isar Generator will do the heavy lifting for you and generate most of the code you need to use the collection.\n\n## Anatomy of a collection\n\nYou define each Isar collection by annotating a class with `@collection` or `@Collection()`. An Isar collection includes fields for each column in the corresponding table in the database, including one that comprises the primary key.\n\nThe following code is an example of a simple collection that defines a `User` table with columns for ID, first name, and last name:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nTo persist a field, Isar must have access to it. You can ensure Isar has access to a field by making it public or by providing getter and setter methods.\n:::\n\nThere are a few optional parameters to customize the collection:\n\n| Config        | Description                                                                                                      |\n| ------------- | ---------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Control whether fields of parent classes and mixins will be stored in Isar. Enabled by default.                  |\n| `accessor`    | Allows you to rename the default collection accessor (for example `isar.contacts` for the `Contact` collection). |\n| `ignore`      | Allows ignoring certain properties. These are also respected for super classes.                                  |\n\n### Isar Id\n\nEach collection class has to define an id property with the type `Id` uniquely identifying an object. `Id` is just an alias for `int` that allows the Isar Generator to recognize the id property.\n\nIsar automatically indexes id fields, which allows you to get and modify objects based on their id efficiently.\n\nYou can either set ids yourself or ask Isar to assign an auto-increment id. If the `id` field is `null` and not `final`, Isar will assign an auto-increment id. If you want a non-nullable auto-increment id, you can use `Isar.autoIncrement` instead of `null`.\n\n:::tip\nAuto increment ids are not reused when an object is deleted. The only way to reset auto-increment ids is to clear the database.\n:::\n\n### Renaming collections and fields\n\nBy default, Isar uses the class name as the collection name. Similarly, Isar uses field names as column names in the database. If you want a collection or field to have a different name, add the `@Name` annotation. The following example demonstrates custom names for collection and fields:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nEspecially if you want to rename Dart fields or classes that are already stored in the database, you should consider using the `@Name` annotation. Otherwise, the database will delete and re-create the field or collection.\n\n### Ignoring fields\n\nIsar persists all public fields of a collection class. By annotating a property or getter with `@ignore`, you can exclude it from persistence, as shown in the following code snippet:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nIn cases where a collection inherits fields from a parent collection, it's usually easier to use the `ignore` property of the `@Collection` annotation:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nIf a collection contains a field with a type that is not supported by Isar, you have to ignore the field.\n\n:::warning\nKeep in mind that it is not good practice to store information in Isar objects that are not persisted.\n:::\n\n## Supported types\n\nIsar supports the following data types:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nAdditionally, embedded objects and enums are supported. We'll cover those below.\n\n## byte, short, float\n\nFor many use cases, you don't need the full range of a 64-bit integer or double. Isar supports additional types that allow you to save space and memory when storing smaller numbers.\n\n| Type       | Size in bytes | Range                                                   |\n| ---------- | ------------- | ------------------------------------------------------- |\n| **byte**   | 1             | 0 to 255                                                |\n| **short**  | 4             | -2,147,483,647 to 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 to 3.4e38                                       |\n| **double** | 8             | -1.7e308 to 1.7e308                                     |\n\nThe additional number types are just aliases for the native Dart types, so using `short`, for example, works the same as using `int`.\n\nHere is an example collection containing all of the above types:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nAll number types can also be used in lists. For storing bytes, you should use `List<byte>`.\n\n## Nullable types\n\nUnderstanding how nullability works in Isar is essential: Number types do **NOT** have a dedicated `null` representation. Instead, a specific value is used:\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`, `String`, and `List` have a separate `null` representation.\n\nThis behavior enables performance improvements, and it allows you to change the nullability of your fields freely without requiring migration or special code to handle `null` values.\n\n:::warning\nThe `byte` type does not support null values.\n:::\n\n## DateTime\n\nIsar does not store timezone information of your dates. Instead, it converts `DateTime`s to UTC before storing them. Isar returns all dates in local time.\n\n`DateTime`s are stored with microsecond precision. In browsers, only millisecond precision is supported because of JavaScript limitations.\n\n## Enum\n\nIsar allows storing and using enums like other Isar types. You have to choose, however, how Isar should represent the enum on the disk. Isar supports four different strategies:\n\n| EnumType    | Description                                                                                         |\n| ----------- | --------------------------------------------------------------------------------------------------- |\n| `ordinal`   | The index of the enum is stored as `byte`. This is very efficient but does not allow nullable enums |\n| `ordinal32` | The index of the enum is stored as `short` (4-byte integer).                                        |\n| `name`      | The enum name is stored as `String`.                                                                |\n| `value`     | A custom property is used to retrieve the enum value.                                               |\n\n:::warning\n`ordinal` and `ordinal32` depend on the order of the enum values. If you change the order, existing databases will return incorrect values.\n:::\n\nLet's check out an example for each strategy.\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // same as EnumType.ordinal\n  late TestEnum byteIndex; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nOf course, Enums can also be used in lists.\n\n## Embedded objects\n\nIt's often helpful to have nested objects in your collection model. There is no limit to how deep you can nest objects. Keep in mind, however, that updating a deeply nested object will require writing the whole object tree to the database.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nEmbedded objects can be nullable and extend other objects. The only requirement is that they are annotated with `@embedded` and have a default constructor without required parameters.\n"
  },
  {
    "path": "docs/docs/pt/transactions.md",
    "content": "---\ntitle: Transações\n---\n\n# Transações\n\nNo Isar, as transações combinam várias operações de banco de dados em uma única unidade de trabalho. A maioria das interações com Isar usa transações implicitamente. O acesso de leitura e gravação no Isar é compatível com [ACID](http://en.wikipedia.org/wiki/ACID). As transações são revertidas automaticamente se ocorrer um erro.\n\n## Transações explicitas\n\nEm uma transação explícita, você obtém um snapShot consistente do banco de dados. Tente minimizar a duração das transações. É proibido fazer chamadas de rede ou outras operações de longa duração em uma transação.\n\nAs transações (especialmente transações de gravação) têm um custo e você deve sempre tentar agrupar operações sucessivas em uma única transação.\n\nAs transações podem ser síncronas ou assíncronas. Em transações síncronas, você só pode usar operações síncronas. Em transações assíncronas, apenas operações assíncronas.\n\n|              | Read         | Read & Write       |\n|--------------|--------------|--------------------|\n| Synchronous  | `.txnSync()` | `.writeTxnSync()`  |\n| Asynchronous | `.txn()`     | `.writeTxn()`      |\n\n\n### Transações de leitura\n\nAs transações de leitura explícita são opcionais, mas permitem que você faça leituras atômicas e dependa de um estado consistente do banco de dados dentro da transação. Internamente, o Isar sempre usa transações de leitura implícitas para todas as operações de leitura.\n\n:::tip\nAs transações de leitura assíncrona são executadas em paralelo com outras transações de leitura e gravação. Bem fixe, certo?\n:::\n\n### Transações de escrita\n\nAo contrário das operações de leitura, as operações de gravação em Isar devem ser agrupadas em uma transação explícita.\n\nQuando uma transação de gravação é concluída com êxito, ela é confirmada automaticamente e todas as alterações são gravadas no disco. Se ocorrer um erro, a transação será abortada e todas as alterações serão revertidas. As transações são “tudo ou nada”: ou todas as gravações em uma transação são bem-sucedidas ou nenhuma delas entra em vigor para garantir a consistência dos dados.\n\n:::warning\nQuando uma operação de banco de dados falha, a transação é abortada e não deve mais ser usada. Mesmo se você pegar o erro no Dart.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: move loop inside transaction\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/pt/tutorials/quickstart.md",
    "content": "---\ntitle: Início rápido\n---\n\n# Início rápido\n\nCaramba, você está aqui! Vamos começar a usar o banco de dados Flutter mais legal que existe...\n\nSeremos curtos em palavras e rápidos em código neste início rápido.\n\n## 1. Adicionar dependências\n\nAntes que a diversão comece, precisamos adicionar alguns pacotes ao `pubspec.yaml`. Podemos usar o pub para fazer o trabalho complexo para nós.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. Anotar classes\n\nAnote suas coleções de classes com `@collection` e escolha um campo 'Id'.\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // você também pode attribuir id = null para incrementar automaticamente\n\n  String? name;\n\n  int? age;\n}\n```\n\nOs IDs identificam exclusivamente objetos em uma coleção e permitem que você os encontre novamente mais tarde.\n\n## 3. Executar gerador de código\n\nExecute o seguinte comando para iniciar o `build_runner`:\n\n```\ndart run build_runner build\n```\n\nSe você estiver usando o Flutter, use o seguinte:\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Abrir instância Isar\n\nAbra uma nova instância Isar e passe todos os seus esquemas de coleção. Opcionalmente, você pode especificar um nome de instância e um diretório.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. Escrever e ler\n\nDepois que sua instância estiver aberta, você poderá começar a usar as coleções.\n\nTodas as operações básicas de CRUD estão disponíveis via `IsarCollection`.\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // inserir & atualizar\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // get\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // delete\n});\n```\n\n## Outros recursos\n\nVocê é um aprendiz visual? Confira estes vídeos para começar com Isar:\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/pt/watchers.md",
    "content": "---\ntitle: Watchers\n---\n\n# Watchers\n\nIsar allows you to subscribe to changes in the database. You can \"watch\" for changes in a specific object, an entire collection, or a query.\n\nWatchers enable you to react to changes in the database efficiently. You can for example rebuild your UI when a contact is added, send a network request when a document is updated, etc.\n\nA watcher is notified after a transaction commits successfully and the target actually changes.\n\n## Watching Objects\n\nIf you want to be notified when a specific object is created, updated or deleted, you should watch an object:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\nAs you can see in the example above, the object does not need to exist yet. The watcher will be notified when it is created.\n\nThere is an additional parameter `fireImmediately`. If you set it to `true`, Isar will immediately add the object's current value to the stream.\n\n### Lazy watching\n\nMaybe you don't need to receive the new value but only be notified about the change. That saves Isar from having to fetch the object:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## Watching Collections\n\nInstead of watching a single object, you can watch an entire collection and get notified when any object is added, updated, or deleted:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## Watching Queries\n\nIt is even possible to watch entire queries. Isar does its best to only notify you when the query results actually change. You will not be notified if links cause the query to change. Use a collection watcher if you need to be notified about link changes.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nIf you use offset & limit or distinct queries, Isar will also notify you when objects match the filter but outside the query, results change.\n:::\n\nJust like `watchObject()`, you can use `watchLazy()` to get notified when the query results change but not fetch the results.\n\n:::danger\nRerunning queries for every change is very inefficient. It would be best if you used a lazy collection watcher instead.\n:::\n"
  },
  {
    "path": "docs/docs/queries.md",
    "content": "---\ntitle: Queries\n---\n\n# Queries\n\nQuerying is how you find records that match certain conditions, for example:\n\n- Find all starred contacts\n- Find distinct first names in contacts\n- Delete all contacts that don't have the last name defined\n\nBecause queries are executed on the database and not in Dart, they're really fast. When you cleverly use indexes, you can improve the query performance even further. In the following, you'll learn how to write queries and how you can make them as fast as possible.\n\nThere are two different methods of filtering your records: Filters and where clauses. We'll start by taking a look at how filters work.\n\n## Filters\n\nFilters are easy to use and understand. Depending on the type of your properties, there are different filter operations available most of which have self-explanatory names.\n\nFilters work by evaluating an expression for every object in the collection being filtered. If the expression resolves to `true`, Isar includes the object in the results. Filters do not affect the ordering of the results.\n\nWe'll use the following model for the examples below:\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### Query conditions\n\nDepending on the type of field, there are different conditions available.\n\n| Condition                | Description                                                                                                                                     |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |\n| `.equalTo(value)`        | Matches values that are equal to the specified `value`.                                                                                         |\n| `.between(lower, upper)` | Matches values that are between `lower` and `upper`.                                                                                            |\n| `.greaterThan(bound)`    | Matches values that are greater than `bound`.                                                                                                   |\n| `.lessThan(bound)`       | Matches values that are less than `bound`. `null` values will be included by default because `null` is considered smaller than any other value. |\n| `.isNull()`              | Matches values that are `null`.                                                                                                                 |\n| `.isNotNull()`           | Matches values that are not `null`.                                                                                                             |\n| `.length()`              | List, String and link length queries filter objects based on the number of elements in a list or link.                                          |\n\nLet's assume the database contains four shoes with sizes 39, 40, 46 and one with an un-set (`null`) size. Unless you perform sorting, the values will be returned sorted by id.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### Logical operators\n\nYou can composite predicates using the following logical operators:\n\n| Operator   | Description                                                                          |\n| ---------- | ------------------------------------------------------------------------------------ |\n| `.and()`   | Evaluates to `true` if both left-hand and right-hand expressions evaluate to `true`. |\n| `.or()`    | Evaluates to `true` if either expression evaluates to `true`.                        |\n| `.xor()`   | Evaluates to `true` if exactly one expression evaluates to `true`.                   |\n| `.not()`   | Negates the result of the following expression.                                      |\n| `.group()` | Group conditions and allow to specify order of evaluation.                           |\n\nIf you want to find all shoes in size 46, you can use the following query:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nIf you want to use more than one condition, you can combine multiple filters using logical **and** `.and()`, logical **or** `.or()` and logical **xor** `.xor()`.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Optional. Filters are implicitly combined with logical and.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nThis query is equivalent to: `size == 46 && isUnisex == true`.\n\nYou can also group conditions using `.group()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nThis query is equivalent to `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nTo negate a condition or group, use logical **not** `.not()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nThis query is equivalent to `size != 46 && isUnisex != true`.\n\n### String conditions\n\nIn addition to the query conditions above, String values offer a few more conditions you can use. Regex-like wildcards, for example, allow more flexibility in search.\n\n| Condition            | Description                                                       |\n| -------------------- | ----------------------------------------------------------------- |\n| `.startsWith(value)` | Matches string values that begins with provided `value`.          |\n| `.contains(value)`   | Matches string values that contain the provided `value`.          |\n| `.endsWith(value)`   | Matches string values that end with the provided `value`.         |\n| `.matches(wildcard)` | Matches string values that match the provided `wildcard` pattern. |\n\n**Case sensitivity**  \nAll string operations have an optional `caseSensitive` parameter that defaults to `true`.\n\n**Wildcards:**  \nA [wildcard string expression](https://en.wikipedia.org/wiki/Wildcard_character) is a string that uses normal characters with two special wildcard characters:\n\n- The `*` wildcard matches zero or more of any character\n- The `?` wildcard matches any character.  \n  For example, the wildcard string `\"d?g\"` matches `\"dog\"`, `\"dig\"`, and `\"dug\"`, but not `\"ding\"`, `\"dg\"`, or `\"a dog\"`.\n\n### Query modifiers\n\nSometimes it is necessary to build a query based on some conditions or for different values. Isar has a very powerful tool for building conditional queries:\n\n| Modifier              | Description                                                                                                                                     |\n| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |\n| `.optional(cond, qb)` | Extends the query only if the `condition` is `true`. This can be used almost anywhere in a query for example to conditionally sort or limit it. |\n| `.anyOf(list, qb)`    | Extends the query for each value in `values` and combines the conditions using logical **or**.                                                  |\n| `.allOf(list, qb)`    | Extends the query for each value in `values` and combines the conditions using logical **and**.                                                 |\n\nIn this example, we build a method that can find shoes with an optional filter:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // only apply filter if sizeFilter != null\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nIf you want to find all shoes that have one of multiple shoe sizes, you can either write a conventional query or use the `anyOf()` modifier:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nQuery modifiers are especially useful when you want to build dynamic queries.\n\n### Lists\n\nEven lists can be queried:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nYou can query based on the list length:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nThese are equivalent to the Dart code `tweets.where((t) => t.hashtags.isEmpty);` and `tweets.where((t) => t.hashtags.length > 5);`. You can also query based on list elements:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nThis is equivalent to the Dart code `tweets.where((t) => t.hashtags.contains('flutter'));`.\n\n### Embedded objects\n\nEmbedded objects are one of Isar's most useful features. They can be queried very efficiently using the same conditions available for top-level objects. Let's assume we have the following model:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nWe want to query all cars that have a brand with the name `\"BMW\"` and the country `\"Germany\"`. We can do this using the following query:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nAlways try to group nested queries. The above query is more efficient than the following one. Even though the result is the same:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### Links\n\nIf your model contains [links or backlinks](links) you can filter your query based on the linked objects or the number of linked objects.\n\n:::warning\nKeep in mind that link queries can be expensive because Isar needs to look up linked objects. Consider using embedded objects instead.\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nWe want to find all students that have a math or English teacher:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nLink filters evaluate to `true` if at least one linked object matches the conditions.\n\nLet's search for all students that have no teachers:\n\n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\nor alternatively:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Where clauses\n\nWhere clauses are a very powerful tool, but it can be a little challenging to get them right.\n\nIn contrast to filters where clauses use the indexes you defined in the schema to check the query conditions. Querying an index is a lot faster than filtering each record individually.\n\n➡️ Learn more: [Indexes](indexes)\n\n:::tip\nAs a basic rule, you should always try to reduce the records as much as possible using where clauses and do the remaining filtering using filters.\n:::\n\nYou can only combine where clauses using logical **or**. In other words, you can sum multiple where clauses together, but you can't query the intersection of multiple where clauses.\n\nLet's add indexes to the shoe collection:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nThere are two indexes. The index on `size` allows us to use where clauses like `.sizeEqualTo()`. The composite index on `isUnisex` allows where clauses like `.isUnisexSizeEqualTo()`. But also `.isUnisexEqualTo()` because you can always use any prefix of an index.\n\nWe can now rewrite the query from before that finds unisex shoes in size 46 using the composite index. This query will be a lot faster than the previous one:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nWhere clauses have two more superpowers: They give you \"free\" sorting and a super fast distinct operation.\n\n### Combining where clauses and filters\n\nRemember the `shoes.filter()` queries? It's actually just a shortcut for `shoes.where().filter()`. You can (and should) combine where clauses and filters in the same query to use the benefits of both:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nThe where clause is applied first to reduce the number of objects to be filtered. Then the filter is applied to the remaining objects.\n\n## Sorting\n\nYou can define how the results should be sorted when executing the query using the `.sortBy()`, `.sortByDesc()`, `.thenBy()` and `.thenByDesc()` methods.\n\nTo find all shoes sorted by model name in ascending order and size in descending order without using an index:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nSorting many results can be expensive, especially since sorting happens before offset and limit. The sorting methods above never make use of indexes. Luckily, we can again use where clause sorting and make our query lightning-fast even if we need to sort a million objects.\n\n### Where clause sorting\n\nIf you use a **single** where clause in your query, the results are already sorted by the index. That's a big deal!\n\nLet's assume we have shoes in sizes `[43, 39, 48, 40, 42, 45]` and we want to find all shoes with a size greater than `42` and also have them sorted by size:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // also sorts the results by size\n  .findAll(); // -> [43, 45, 48]\n```\n\nAs you can see, the result is sorted by the `size` index. If you want to reverse the where clause sort order, you can set `sort` to `Sort.desc`:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nSometimes you don't want to use a where clause but still benefit from the implicit sorting. You can use the `any` where clause:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nIf you use a composite index, the results are sorted by all fields in the index.\n\n:::tip\nIf you need the results to be sorted, consider using an index for that purpose. Especially if you work with `offset()` and `limit()`.\n:::\n\nSometimes it's not possible or useful to use an index for sorting. For such cases, you should use indexes to reduce the number of resulting entries as much as possible.\n\n## Unique values\n\nTo return only entries with unique values, use the distinct predicate. For example, to find out how many different shoe models you have in your Isar database:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nYou can also chain multiple distinct conditions to find all shoes with distinct model-size combinations:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nOnly the first result of each distinct combination is returned. You can use where clauses and sort operations to control it.\n\n### Where clause distinct\n\nIf you have a non-unique index, you may want to get all of its distinct values. You could use the `distinctBy` operation from the previous section, but it's performed after sorting and filters, so there is some overhead.  \nIf you only use a single where clause, you can instead rely on the index to perform the distinct operation.\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nIn theory, you could even use multiple where clauses for sorting and distinct. The only restriction is that those where clauses are not overlapping and use the same index. For correct sorting, they also need to be applied in sort order. Be very careful if you rely on this!\n:::\n\n## Offset & Limit\n\nIt's often a good idea to limit the number of results from a query for lazy list views. You can do so by setting a `limit()`:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nBy setting an `offset()` you can also paginate the results of your query.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nSince instantiating Dart objects is often the most expensive part of executing a query, it is a good idea only to load the objects you need.\n\n## Execution order\n\nIsar executes queries always in the same order:\n\n1. Traverse primary or secondary index to find objects (apply where clauses)\n2. Filter objects\n3. Sort results\n4. Apply distinct operation\n5. Offset & limit results\n6. Return results\n\n## Query operations\n\nIn the previous examples, we used `.findAll()` to retrieve all matching objects. There are more operations available, however:\n\n| Operation        | Description                                                                                                         |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | Retrieve only the first matching object or `null` if none matches.                                                  |\n| `.findAll()`     | Retrieve all matching objects.                                                                                      |\n| `.count()`       | Count how many objects match the query.                                                                             |\n| `.deleteFirst()` | Delete the first matching object from the collection.                                                               |\n| `.deleteAll()`   | Delete all matching objects from the collection.                                                                    |\n| `.build()`       | Compile the query to reuse it later. This saves the cost to build a query if you want to execute it multiple times. |\n\n## Property queries\n\nIf you are only interested in the values of a single property, you can use a property query. Just build a regular query and select a property:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nUsing only a single property saves time during deserialization. Property queries also work for embedded objects and lists.\n\n## Aggregation\n\nIsar supports aggregating the values of a property query. The following aggregation operations are available:\n\n| Operation    | Description                                                    |\n| ------------ | -------------------------------------------------------------- |\n| `.min()`     | Finds the minimum value or `null` if none matches.             |\n| `.max()`     | Finds the maximum value or `null` if none matches.             |\n| `.sum()`     | Sums all values.                                               |\n| `.average()` | Calculates the average of all values or `NaN` if none matches. |\n\nUsing aggregations is vastly faster than finding all matching objects and performing the aggregation manually.\n\n## Dynamic queries\n\n:::danger\nThis section is most likely not relevant to you. It is discouraged to use dynamic queries unless you absolutely need to (and you rarely do).\n:::\n\nAll the examples above used the QueryBuilder and the generated static extension methods. Maybe you want to create dynamic queries or a custom query language (like the Isar Inspector). In that case, you can use the `buildQuery()` method:\n\n| Parameter       | Description                                                                                 |\n| --------------- | ------------------------------------------------------------------------------------------- |\n| `whereClauses`  | The where clauses of the query.                                                             |\n| `whereDistinct` | Whether where clauses should return distinct values (only useful for single where clauses). |\n| `whereSort`     | The traverse order of the where clauses (only useful for single where clauses).             |\n| `filter`        | The filter to apply to the results.                                                         |\n| `sortBy`        | A list of properties to sort by.                                                            |\n| `distinctBy`    | A list of properties to distinct by.                                                        |\n| `offset`        | The offset of the results.                                                                  |\n| `limit`         | The maximum number of results to return.                                                    |\n| `property`      | If non-null, only the values of this property are returned.                                 |\n\nLet's create a dynamic query:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nThe following query is equivalent:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/recipes/data_migration.md",
    "content": "---\ntitle: Data migration\n---\n\n# Data Migration\n\nIsar automatically migrates your database schemas if you add or remove collections, fields, or indexes. Sometimes you might want to migrate your data as well. Isar does not offer a built-in solution because it would impose arbitrary migration restrictions. It is easy to implement migration logic that fits your needs.\n\nWe want to use a single version for the entire database in this example. We use shared preferences to store the current version and compare it to the version we want to migrate to. If the versions do not match, we migrate the data and update the version.\n\n:::tip\nYou could also give each collection its own version and migrate them individually.\n:::\n\nImagine we have a user collection with a birthday field. In version 2 of our app, we need an additional birth year field to query users based on age.\n\nVersion 1:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersion 2:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\nThe problem is the existing user models will have an empty `birthYear` field because it did not exist in version 1. We need to migrate the data to set the `birthYear` field.\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // If the version is not set (new installation) or already 2, we do not need to migrate\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // Update version\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // We paginate through the users to avoid loading all users into memory at once\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // We don't need to update anything since the birthYear getter is used\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\nIf you have to migrate a lot of data, consider using a background isolate to prevent strain on the UI thread.\n:::\n"
  },
  {
    "path": "docs/docs/recipes/full_text_search.md",
    "content": "---\ntitle: Full-text search\n---\n\n# Full-text search\n\nFull-text search is a powerful way to search text in the database. You should already be familiar with how [indexes](/indexes) work, but let's go over the basics.\n\nAn index works like a lookup table, allowing the query engine to find records with a given value quickly. For example, if you have a `title` field in your object, you can create an index on that field to make it faster to find objects with a given title.\n\n## Why is full-text search useful?\n\nYou can easily search text using filters. There are various string operations for example `.startsWith()`, `.contains()` and `.matches()`. The problem with filters is that their runtime is `O(n)` where `n` is the number of records in the collection. String operations like `.matches()` are especially expensive.\n\n:::tip\nFull-text search is much faster than filters, but indexes have some limitations. In this recipe, we will explore how to work around these limitations.\n:::\n\n## Basic example\n\nThe idea is always the same: Instead of indexing the whole text, we index the words in the text so we can search for them individually.\n\nLet's create the most basic full-text index:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nWe can now search for messages with specific words in the content:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nThis query is super fast, but there are some problems:\n\n1. We can only search for entire words\n2. We do not consider punctuation\n3. We do not support other whitespace characters\n\n## Splitting text the right way\n\nLet's try to improve the previous example. We could try to develop a complicated regex to fix word splitting, but it will likely be slow and wrong for edge cases.\n\nThe [Unicode Annex #29](https://unicode.org/reports/tr29/) defines how to split text into words correctly for almost all languages. It is quite complicated, but fortunately, Isar does the heavy lifting for us:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## I want more control\n\nEasy peasy! We can change our index also to support prefix matching and case-insensitive matching:\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nBy default, Isar will store the words as hashed values which is fast and space efficient. But hashes can't be used for prefix matching. Using `IndexType.value`, we can change the index to use the words directly instead. It gives us the `.titleWordsAnyStartsWith()` where clause:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## I also need `.endsWith()`\n\nSure thing! We will use a trick to achieve `.endsWith()` matching:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nDon't forget reversing the ending you want to search for:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Stemming algorithms\n\nUnfortunately, indexes do not support `.contains()` matching (this is true for other databases as well). But there are a few alternatives that are worth exploring. The choice highly depends on your use. One example is indexing word stems instead of the whole word.\n\nA stemming algorithm is a process of linguistic normalization in which the variant forms of a word are reduced to a common form:\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\nPopular algorithms are the [Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) and the [Snowball stemming algorithms](https://snowballstem.org/algorithms/).\n\nThere are also more advanced forms like [lemmatization](https://en.wikipedia.org/wiki/Lemmatisation).\n\n## Phonetic algorithms\n\nA [phonetic algorithm](https://en.wikipedia.org/wiki/Phonetic_algorithm) is an algorithm for indexing words by their pronunciation. In other words, it allows you to find words that sound similar to the ones you are looking for.\n\n:::warning\nMost phonetic algorithms only support a single language.\n:::\n\n### Soundex\n\n[Soundex](https://en.wikipedia.org/wiki/Soundex) is a phonetic algorithm for indexing names by sound, as pronounced in English. The goal is for homophones to be encoded to the same representation so they can be matched despite minor differences in spelling. It is a straightforward algorithm, and there are multiple improved versions.\n\nUsing this algorithm, both `\"Robert\"` and `\"Rupert\"` return the string `\"R163\"` while `\"Rubin\"` yields `\"R150\"`. `\"Ashcraft\"` and `\"Ashcroft\"` both yield `\"A261\"`.\n\n### Double Metaphone\n\nThe [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) phonetic encoding algorithm is the second generation of this algorithm. It makes several fundamental design improvements over the original Metaphone algorithm.\n\nDouble Metaphone accounts for various irregularities in English of Slavic, Germanic, Celtic, Greek, French, Italian, Spanish, Chinese, and other origins.\n"
  },
  {
    "path": "docs/docs/recipes/multi_isolate.md",
    "content": "---\ntitle: Multi-Isolate usage\n---\n\n# Multi-Isolate usage\n\nInstead of threads, all Dart code runs inside isolates. Each isolate has its own memory heap, ensuring that none of the state in an isolate is accessible from any other isolate.\n\nIsar can be accessed from multiple isolates at the same time, and even watchers work across isolates. In this recipe, we will check out how to use Isar in a multi-isolate environment.\n\n## When to use multiple isolates\n\nIsar transactions are executed in parallel even if they run in the same isolate. In some cases, it is still beneficial to access Isar from multiple isolates.\n\nThe reason is that Isar spends quite some time encoding and decoding data from and to Dart objects. You can think of it as encoding and decoding JSON (just more efficient). These operations run inside the isolate from which the data is accessed and naturally block other code in the isolate. In other words: Isar performs some of the work in your Dart isolate.\n\nIf you only need to read or write a few hundred objects at once, doing it in the UI isolate is not a problem. But for huge transactions or if the UI thread is already busy, you should consider using a separate isolate.\n\n## Example\n\nThe first thing we need to do is to open Isar in the new isolate. Since the instance of Isar is already open in the main isolate, `Isar.open()` will return the same instance.\n\n:::warning\nMake sure to provide the same schemas as in the main isolate. Otherwise, you will get an error.\n:::\n\n`compute()` starts a new isolate in Flutter and runs the given function in it.\n\n```dart\nvoid main() {\n  // Open Isar in the UI isolate\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // listen to changes in the database\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // start a new isolate and create 10000 messages\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // after some time:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// function that will be executed in the new isolate\nFuture createDummyMessages(int count) async {\n  // we don't need the path here because the instance is already open\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // we use synchronous transactions in isolates\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nThere are a few interesting things to note in the example above:\n\n- `isar.messages.watchLazy()` is called in the UI isolate and is notified of changes from another isolate.\n- Instances are referenced by name. The default name is `default`, but in this example, we set it to `myInstance`.\n- We used a synchronous transaction to create the messages. Blocking our new isolate is no problem, and synchronous transactions are a little faster.\n"
  },
  {
    "path": "docs/docs/recipes/string_ids.md",
    "content": "---\ntitle: String ids\n---\n\n# String ids\n\nThis is one of the most frequent requests I get, so here is a tutorial on using String ids.\n\nIsar does not natively support String ids, and there is a good reason for it: integer ids are much more efficient and faster. Especially for links, the overhead of a String id is too significant.\n\nI understand that sometimes you have to store external data that uses UUIDs or other non-integer ids. I recommend storing the String id as a property in your object and using a fast hash implementation to generate a 64-bit int that can be used as Id.\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nWith this approach, you get the best of both worlds: Efficient integer ids for links and the ability to use String ids.\n\n## Fast hash function\n\nIdeally, your hash function should have high quality (you don't want collisions) and be fast. I recommend using the following implementation:\n\n```dart\n/// FNV-1a 64bit hash algorithm optimized for Dart Strings\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\nIf you choose a different hash function, ensure it returns a 64-bit int and avoid using a cryptographic hash function because they are much slower.\n\n:::warning\nAvoid using `string.hashCode` because it is not guaranteed to be stable across different platforms and versions of Dart.\n:::\n"
  },
  {
    "path": "docs/docs/schema.md",
    "content": "---\ntitle: Schema\n---\n\n# Schema\n\nWhen you use Isar to store your app's data, you're dealing with collections. A collection is like a database table in the associated Isar database and can only contain a single type of Dart object. Each collection object represents a row of data in the corresponding collection.\n\nA collection definition is called \"schema\". The Isar Generator will do the heavy lifting for you and generate most of the code you need to use the collection.\n\n## Anatomy of a collection\n\nYou define each Isar collection by annotating a class with `@collection` or `@Collection()`. An Isar collection includes fields for each column in the corresponding table in the database, including one that comprises the primary key.\n\nThe following code is an example of a simple collection that defines a `User` table with columns for ID, first name, and last name:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nTo persist a field, Isar must have access to it. You can ensure Isar has access to a field by making it public or by providing getter and setter methods.\n:::\n\nThere are a few optional parameters to customize the collection:\n\n| Config        | Description                                                                                                      |\n| ------------- | ---------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | Control whether fields of parent classes and mixins will be stored in Isar. Enabled by default.                  |\n| `accessor`    | Allows you to rename the default collection accessor (for example `isar.contacts` for the `Contact` collection). |\n| `ignore`      | Allows ignoring certain properties. These are also respected for super classes.                                  |\n\n### Isar Id\n\nEach collection class has to define an id property with the type `Id` uniquely identifying an object. `Id` is just an alias for `int` that allows the Isar Generator to recognize the id property.\n\nIsar automatically indexes id fields, which allows you to get and modify objects based on their id efficiently.\n\nYou can either set ids yourself or ask Isar to assign an auto-increment id. If the `id` field is `null` and not `final`, Isar will assign an auto-increment id. If you want a non-nullable auto-increment id, you can use `Isar.autoIncrement` instead of `null`.\n\n:::tip\nAuto increment ids are not reused when an object is deleted. The only way to reset auto-increment ids is to clear the database.\n:::\n\n### Renaming collections and fields\n\nBy default, Isar uses the class name as the collection name. Similarly, Isar uses field names as column names in the database. If you want a collection or field to have a different name, add the `@Name` annotation. The following example demonstrates custom names for collection and fields:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nEspecially if you want to rename Dart fields or classes that are already stored in the database, you should consider using the `@Name` annotation. Otherwise, the database will delete and re-create the field or collection.\n\n### Ignoring fields\n\nIsar persists all public fields of a collection class. By annotating a property or getter with `@ignore`, you can exclude it from persistence, as shown in the following code snippet:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nIn cases where a collection inherits fields from a parent collection, it's usually easier to use the `ignore` property of the `@Collection` annotation:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nIf a collection contains a field with a type that is not supported by Isar, you have to ignore the field.\n\n:::warning\nKeep in mind that it is not good practice to store information in Isar objects that are not persisted.\n:::\n\n## Supported types\n\nIsar supports the following data types:\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nAdditionally, embedded objects and enums are supported. We'll cover those below.\n\n## byte, short, float\n\nFor many use cases, you don't need the full range of a 64-bit integer or double. Isar supports additional types that allow you to save space and memory when storing smaller numbers.\n\n| Type       | Size in bytes | Range                                                   |\n| ---------- | ------------- | ------------------------------------------------------- |\n| **byte**   | 1             | 0 to 255                                                |\n| **short**  | 4             | -2,147,483,647 to 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 to 3.4e38                                       |\n| **double** | 8             | -1.7e308 to 1.7e308                                     |\n\nThe additional number types are just aliases for the native Dart types, so using `short`, for example, works the same as using `int`.\n\nHere is an example collection containing all of the above types:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nAll number types can also be used in lists. For storing bytes, you should use `List<byte>`.\n\n## Nullable types\n\nUnderstanding how nullability works in Isar is essential: Number types do **NOT** have a dedicated `null` representation. Instead, a specific value is used:\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`, `String`, and `List` have a separate `null` representation.\n\nThis behavior enables performance improvements, and it allows you to change the nullability of your fields freely without requiring migration or special code to handle `null` values.\n\n:::warning\nThe `byte` type does not support null values.\n:::\n\n## DateTime\n\nIsar does not store timezone information of your dates. Instead, it converts `DateTime`s to UTC before storing them. Isar returns all dates in local time.\n\n`DateTime`s are stored with microsecond precision. In browsers, only millisecond precision is supported because of JavaScript limitations.\n\n## Enum\n\nIsar allows storing and using enums like other Isar types. You have to choose, however, how Isar should represent the enum on the disk. Isar supports four different strategies:\n\n| EnumType    | Description                                                                                         |\n| ----------- | --------------------------------------------------------------------------------------------------- |\n| `ordinal`   | The index of the enum is stored as `byte`. This is very efficient but does not allow nullable enums |\n| `ordinal32` | The index of the enum is stored as `short` (4-byte integer).                                        |\n| `name`      | The enum name is stored as `String`.                                                                |\n| `value`     | A custom property is used to retrieve the enum value.                                               |\n\n:::warning\n`ordinal` and `ordinal32` depend on the order of the enum values. If you change the order, existing databases will return incorrect values.\n:::\n\nLet's check out an example for each strategy.\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // same as EnumType.ordinal\n  late TestEnum byteIndex; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nOf course, Enums can also be used in lists.\n\n## Embedded objects\n\nIt's often helpful to have nested objects in your collection model. There is no limit to how deep you can nest objects. Keep in mind, however, that updating a deeply nested object will require writing the whole object tree to the database.\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nEmbedded objects can be nullable and extend other objects. The only requirement is that they are annotated with `@embedded` and have a default constructor without required parameters.\n"
  },
  {
    "path": "docs/docs/transactions.md",
    "content": "---\ntitle: Transactions\n---\n\n# Transactions\n\nIn Isar, transactions combine multiple database operations in a single unit of work. Most interactions with Isar implicitly use transactions. Read & write access in Isar is [ACID](http://en.wikipedia.org/wiki/ACID) compliant. Transactions are automatically rolled back if an error occurs.\n\n## Explicit transactions\n\nIn an explicit transaction, you get a consistent snapshot of the database. Try to minimize the duration of transactions. It is forbidden to do network calls or other long-running operations in a transaction.\n\nTransactions (especially write transactions) do have a cost, and you should always try to group successive operations into a single transaction.\n\nTransactions can either be synchronous or asynchronous. In synchronous transactions, you may only use synchronous operations. In asynchronous transactions, only async operations.\n\n|              | Read         | Read & Write      |\n| ------------ | ------------ | ----------------- |\n| Synchronous  | `.txnSync()` | `.writeTxnSync()` |\n| Asynchronous | `.txn()`     | `.writeTxn()`     |\n\n### Read transactions\n\nExplicit read transactions are optional, but they allow you to do atomic reads and rely on a consistent state of the database inside the transaction. Internally Isar always uses implicit read transactions for all read operations.\n\n:::tip\nAsync read transactions run in parallel to other read and write transactions. Pretty cool, right?\n:::\n\n### Write transactions\n\nUnlike read operations, write operations in Isar must be wrapped in an explicit transaction.\n\nWhen a write transaction finishes successfully, it is automatically committed, and all changes are written to disk. If an error occurs, the transaction is aborted, and all the changes are rolled back. Transactions are “all or nothing”: either all the writes within a transaction succeed, or none of them take effect to guarantee data consistency.\n\n:::warning\nWhen a database operation fails, the transaction is aborted and must no longer be used. Even if you catch the error in Dart.\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: move loop inside transaction\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/tutorials/quickstart.md",
    "content": "---\ntitle: Quickstart\n---\n\n# Quickstart\n\nHoly smokes, you're here! Let's get started on using the coolest Flutter database out there...\n\nWe're going to be short on words and quick on code in this quickstart.\n\n## 1. Add dependencies\n\nBefore the fun begins, we need to add a few packages to the `pubspec.yaml`. We can use pub to do the heavy lifting for us.\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. Annotate classes\n\nAnnotate your collection classes with `@collection` and choose an `Id` field.\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // you can also use id = null to auto increment\n\n  String? name;\n\n  int? age;\n}\n```\n\nIds uniquely identify objects in a collection and allow you to find them again later.\n\n## 3. Run code generator\n\nExecute the following command to start the `build_runner`:\n\n```\ndart run build_runner build\n```\n\nIf you are using Flutter, use the following:\n\n```\nflutter pub run build_runner build\n```\n\n## 4. Open Isar instance\n\nOpen a new Isar instance and pass all of your collection schemas. Optionally you can specify an instance name and directory.\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. Write and read\n\nOnce your instance is open, you can start using the collections.\n\nAll basic CRUD operations are available via the `IsarCollection`.\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // insert & update\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // get\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // delete\n});\n```\n\n## Other resources\n\nAre you a visual learner? Check out these videos to get started with Isar:\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/ur/README.md",
    "content": "---\nhome: true\ntitle: ہوم\nheroImage: /isar.svg\nactions:\n  - text:  آئیے شروع کریں۔\n    link: /tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title:  کے لیے بنایا گیاہے💙 Flutter\n    details:  کم سے کم سیٹ اپ، استعمال میں آسان، کوئی ترتیب نہیں، کوئی بوائلر پلیٹ نہیں۔ شروع کرنے کے لیے بس کوڈ کی چند لائنیں شامل کریں۔\n  - title: 🚀 انتہائی قابل توسیع\n    details:  ایک ہی نو ایس کیو ایل ڈیٹا بیس میں سیکڑوں ہزاروں ریکارڈز کو ذخیرہ کریں اور ان سے موثر اور متضاد طور پر استفسار کریں۔\n  - title: 🍭 خصوصیت سے بھرپور\n    details:  آپ کے ڈیٹا کو منظم کرنے میں آپ کی مدد کرنے کے لیے ای زار کے پاس خصوصیات کا ایک بھرپور مجموعہ ہے۔ کمپوزٹ اور ملٹی انٹری انڈیکس، استفسار میں ترمیم کرنے والے، جےسن سپورٹ، اور بہت کچھ۔\n  - title: 🔎 مکمل متن کی تلاش\n    details:  ای زار کے پاس بنی بنائں مکمل متن تلاشی ہے۔ ملٹی انٹری انڈیکس بنائیں اور آسانی سے ریکارڈ تلاش کریں۔\n  - title:  🧪ایسڈ سیمنٹکس\n    details:  ای زار تیزاب کے مطابق ہے اور لین دین کو خود بخود ہینڈل کرتا ہے۔ اگر کوئی خرابی پیش آتی ہے تو یہ تبدیلیوں کو واپس لے لیتا ہے۔\n  - title: 💃 جامد ٹائپنگ\n    details: ای زار کے سوالات کو جامد طور پر ٹائپ کیا جاتا ہے اور مرتب وقت کی جانچ پڑتال کی جاتی ہے۔ رن ٹائم غلطیوں کے بارے میں فکر کرنے کی ضرورت نہیں ہے۔ \n  - title: 📱 ملٹی پلیٹ فارم\n    details: iOS, Android, Desktop, اور مکمل WEB SUPPORT!\n  - title: ⏱ غیر مطابقت پذیر\n    details:  متوازی استفسار کے آپریشنز اور ملٹی آئسولیٹ سپورٹ آؤٹ آف دی باکس\n  - title: 🦄 اوپن سورس\n    details: سب کچھ اوپن سورس اور ہمیشہ کے لیے مفت ہے!\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/ur/crud.md",
    "content": "---\ntitle: بنائیں، پڑھیں، اپ ڈیٹ کریں، حذف کریں\n---\n\n# بنائیں، پڑھیں، اپ ڈیٹ کریں، حذف کریں\n\nجب آپ نے اپنے کلیکشنز کی وضاحت کی ہے، تو سیکھیں کہ انہیں کیسے جوڑنا ہے!\n\n## ای زار کھولنا\n\nاس سے پہلے کہ آپ کچھ کر سکیں، ہمیں ای زار کی مثال درکار ہے۔ ہر مثال کے لیے لکھنے کی اجازت کے ساتھ ڈائرکٹری کی ضرورت ہوتی ہے جہاں ڈیٹا بیس فائل کو محفوظ کیا جا سکتا ہے۔ اگر آپ ڈائرکٹری کی وضاحت نہیں کرتے ہیں، تو ای زار موجودہ پلیٹ فارم کے لیے ایک مناسب ڈیفالٹ ڈائریکٹری تلاش کرے گا۔\n\nوہ تمام اسکیمے فراہم کریں جو آپ ای زار مثال کے ساتھ استعمال کرنا چاہتے ہیں۔ اگر آپ متعدد مثالوں کو کھولتے ہیں، تو آپ کو اب بھی ہر ایک مثال کے لیے ایک ہی اسکیما فراہم کرنا ہوں گی۔\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [RecipeSchema],\n  directory: dir.path,\n);\n```\nآپ پہلے سے طے شدہ تشکیل استعمال کرسکتے ہیں یا درج ذیل میں سے کچھ پیرامیٹرز فراہم کرسکتے ہیں۔\n\nترتیب | تفصیل |\n| -------| -------------|\n| نام | الگ الگ ناموں کے ساتھ متعدد مثالیں کھولیں۔بذریعہ ڈیفالٹ، `\"ڈیفالٹ\"` استعمال ہوتا ہے۔ |\n|ڈائریکٹری | اس مثال کے لیے اسٹوریج کا مقام۔ بطور ڈیفالٹ، آئی او ایس کے لیے `این ایس ڈاکومینٹ ڈائریکٹری` اور اینڈرائڈ کے لیے `گٹ ڈیٹا ڈائریکٹری` استعمال کیا جاتا ہے۔ ویب کے لیے ضروری نہیں ہے۔ |\n|آرام پائیدار | تحریری کارکردگی کو بڑھانے کے لیے پائیداری کی ضمانت کو آرام دیتا ہے۔ سسٹم کریش ہونے کی صورت میں (ایپ کریش نہیں)، آخری کمٹڈ ٹرانزیکشن سے محروم ہونا ممکن ہے۔ کرپشن ممکن نہیں |\n| کمپیکٹ اون لانچ | یہ چیک کرنے کی شرائط کہ آیا مثال کے کھولنے پر ڈیٹا بیس کو کمپیکٹ کیا جانا چاہیے۔ |\n| انسپکٹر | ڈیبگ بلڈز کے لیے انسپکٹر کو فعال کیا۔ پروفائل اور ریلیز کے لیے اس اختیار کو نظر انداز کر دیا گیا ہے۔ |\n\nاگر کوئی مثال پہلے سے کھلی ہوئی ہے تو، 'ای زار.کھولیں()'  کو کال کرنے سے مخصوص پیرامیٹرز سے قطع نظر موجودہ مثال حاصل ہو جائے گی۔ یہ ای زار کو الگ تھلگ میں استعمال کرنے کے لیے مفید ہے۔\n\n:::ٹپ\nتمام پلیٹ فارمز پر درست راستہ حاصل کرنے کے لیے [path_provider](https://pub.dev/packages/path_provider) پیکیج استعمال کرنے پر غور کریں۔\n:::\n\n`directory/name.isar` \nڈیٹا بیس فائل کا سٹوریج لوکیشن \nہے۔\n\n## ڈیٹا بیس سے پڑھنا\n\nای زار میں دی گئی قسم کی نئی اشیاء تلاش کرنے، استفسار کرنے اور تخلیق کرنے کے لیے `ای زار کلیکشن` مثالیں استعمال کریں۔\n\nذیل میں دی گئی مثالوں کے لیے، ہم فرض کرتے ہیں کہ ہمارے پاس ایک مجموعہ ہے 'رےسیپ' کی وضاحت اس طرح کی گئی ہے:\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### ایک مجموعہ حاصل کریں۔\n\nآپ کے تمام مجموعے ایزار مثال میں رہتے ہیں۔ آپ ترکیبوں کا مجموعہ اس کے ساتھ حاصل کرسکتے ہیں:\n\n```dart\nfinal recipes = isar.recipes;\n```\nیہ آسان تھا! اگر آپ کلیکشن ایکسیسرز استعمال نہیں کرنا چاہتے تو آپ `کلیکشن()` طریقہ بھی استعمال کر سکتے ہیں:\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### کوئی چیز حاصل کریں (بذریعہ ID)\n\nہمارے پاس ابھی تک ڈیٹا جمع نہیں ہے لیکن آئیے دکھاوا کرتے ہیں کہ ہم ایسا کرتے ہیں تاکہ ہم 123 آئی ڈی کے ذریعے ایک خیالی چیز حاصل کر سکیں۔\n```dart\nfinal recipe = await isar.recipes.get(123);\n```\n`گیٹ()` کسی بھی چیز کے ساتھ `فیوچر` لوٹاتا ہے یا `نل` اگر یہ موجود نہیں ہے۔ ایزار کے تمام آپریشنز بطور ڈیفالٹ غیر مطابقت پذیر ہوتے ہیں، اور ان میں سے اکثر کا ہم وقتی ہم منصب ہوتا ہے:\n\n```dart\nfinal recipe = isar.recipes.getSync(123);\n```\n:::warning\nآپ کو اپنے یوآئی الگ تھلگ میں طریقوں کے غیر مطابقت پذیر ورژن پر ڈیفالٹ کرنا چاہئے۔ چونکہ ایزار بہت تیز ہے، یہ اکثر مطابقت پذیر ورژن استعمال کرنے کے لئے قابل قبول ہے.\n:::\n\nاگر آپ ایک ساتھ ایک سے زیادہ اشیاء حاصل کرنا چاہتے ہیں تو `گٹ آل ()` یا `گٹ آل سنک ()` استعمال کریں:\n\n```dart\nfinal recipe = await isar.recipes.getAll([1, 2]);\n```\n\n### اشیاء سے استفسار کریں۔\n\nآی ڈی کے ذریعے آبجیکٹ حاصل کرنے کے بجائے آپ `.ویئر()` اور `.فیلٹر()` کا استعمال کرتے ہوئے مخصوص شرائط سے مماثل اشیاء کی فہرست سے بھی استفسار کر سکتے ہیں:\n\n```dart\nfinal allRecipes = await isar.recipes.where().findAll();\n\nfinal favouires = await isar.recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ مزید جانیں: [Queries](queries)\n\n## ڈیٹا بیس میں ترمیم کرنا\n\nآخر کار ہمارے کلیکشن میں ترمیم کرنے کا وقت آگیا ہے! اوبجیکٹس بنانے، اپ ڈیٹ کرنے یا حذف کرنے کے لیے، تحریری لین دین میں لپیٹے ہوئے متعلقہ آپریشنز کا استعمال کریں:\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await isar.recipes.get(123)\n\n  recipe.isFavorite = false;\n  await isar.recipes.put(recipe); // perform update operations\n\n  await isar.recipes.delete(123); // or delete operations\n});\n```\n\n➡️ مزید جانیں:  [Transactions](transactions)\n\n### آبجیکٹ داخل کریں۔\n\nایزار میں کسی چیز کو برقرار رکھنے کے لیے، اسے ایک کلیکشن میں داخل کریں۔ ایزار کا `پٹ()` طریقہ یا تو آبجیکٹ کو داخل یا اپ ڈیٹ کرے گا اس پر منحصر ہے کہ آیا یہ پہلے سے مجموعہ میں موجود ہے۔\n\nاگر آئی ڈی فیلڈ `نل` یا `ایزار.آٹوانکریمنٹ` ہے تو ایزار ایک خودکار اضافہ آئی ڈی استعمال کرے گا۔\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await isar.recipes.put(pancakes);\n})\n```\n\nایزار خود بخود آئی ڈی  آبجیکٹ کو تفویض کر دے گا اگر `آئی ڈی` فیلڈ غیر حتمی ہے۔\n\nایک ساتھ متعدد اشیاء کو داخل کرنا اتنا ہی آسان ہے:\n\n```dart\nawait isar.writeTxn(() async {\n  await isar.recipes.putAll([pancakes, pizza]);\n})\n```\n\n### آبجیکٹ کو اپ ڈیٹ کریں۔\n\nدونوں کو بنانا اور اپ ڈیٹ کرنا `کلیکشن.پت(ااوبجکٹ)` کے ساتھ کام کرتا ہے۔ اگر آئی ڈی نل ہے (یا موجود نہیں ہے) تو آبجیکٹ داخل کیا جاتا ہے۔ دوسری صورت میں، یہ اپ ڈیٹ کیا جاتا ہے.\n\nلہذا اگر ہم اپنے پان کیکس کو ناپسند کرنا چاہتے ہیں، تو ہم درج ذیل کام کر سکتے ہیں:\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await isar.recipes.put(recipe);\n});\n```\n### آبجیکٹ کو حذف کریں۔\n\nایزار میں کسی چیز سے چھٹکارا حاصل کرنا چاہتے ہیں؟ `کلیکشن.ڈیلیٹ(آئی ڈی)` استعمال کریں۔ حذف کرنے کا طریقہ واپس کرتا ہے کہ آیا مخصوص آئی ڈی کے ساتھ کوئی آبجیکٹ ملا اور حذف کر دیا گیا تھا۔ اگر آپ آئی ڈی `123` کے ساتھ آبجیکٹ کو حذف کرنا چاہتے ہیں، مثال کے طور پر، آپ یہ کر سکتے ہیں:\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await isar.recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\nاسی طرح حاصل کرنے اور ڈالنے کے لئے، ایک بلک ڈیلیٹ آپریشن بھی ہے جو حذف شدہ اشیاء کی تعداد لوٹاتا ہے:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\nاگر آپ ان اشیاء کی آئی ڈی نہیں جانتے جنہیں آپ حذف کرنا چاہتے ہیں، تو آپ ایک استفسار استعمال کر سکتے ہیں:\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/ur/faq.md",
    "content": "---\ntitle: اکثر پوچھے گئے سوالات\n---\n\n# اکثر پوچھے گئے سوالات\n\nای زار اور فلٹر ڈیٹا بیس کے بارے میں اکثر پوچھے جانے والے سوالات کا بے ترتیب مجموعہ۔\n\n### مجھے ڈیٹا بیس کی ضرورت کیوں ہے؟\n\n> میں اپنا ڈیٹا بیک اینڈ ڈیٹا بیس میں محفوظ کرتا ہوں، مجھے اسار کی ضرورت کیوں ہے؟\n\nآج بھی، اگر آپ سب وے یا ہوائی جہاز میں ہیں یا اگر آپ اپنی دادی سے ملنے جاتے ہیں، جن کے پاس وائی فائی نہیں ہے اور سیل سگنل بہت خراب ہے۔ آپ کو خراب کنکشن کو اپنی ایپ کو معذور نہیں ہونے دینا چاہیے!\n\n### Isar vs Hive\n\nThe answer is easy: Isar was [started as a replacement for Hive](https://github.com/hivedb/hive/issues/246) and is now at a state where I recommend always using Isar over Hive.\n\n### کہاں کی شقیں؟!\n\n> **_I_** کو یہ کیوں منتخب کرنا ہوگا کہ کون سا انڈیکس استعمال کرنا ہے؟\n\nمتعدد وجوہات ہیں۔ بہت سے ڈیٹا بیس دی گئی استفسار کے لیے بہترین انڈیکس کا انتخاب کرنے کے لیے ہیورسٹکس کا استعمال کرتے ہیں۔ ڈیٹا بیس کو اضافی استعمال کا ڈیٹا جمع کرنے کی ضرورت ہے (-> اوور ہیڈ) اور پھر بھی غلط انڈیکس کا انتخاب کر سکتا ہے۔ یہ استفسار کی تخلیق کو بھی سست بناتا ہے۔\n\nآپ کے ڈیٹا کو آپ سے بہتر کوئی نہیں جانتا، ڈویلپر۔ لہذا آپ بہترین انڈیکس کا انتخاب کر سکتے ہیں اور مثال کے طور پر فیصلہ کر سکتے ہیں کہ آیا آپ استفسار یا چھانٹنے کے لیے انڈیکس استعمال کرنا چاہتے ہیں۔\n\n### کیا مجھے اشاریہ جات / جہاں شقیں استعمال کرنی ہیں؟\n\nنھیں کیا! اگر آپ صرف فلٹرز پر بھروسہ کرتے ہیں تو اسر کافی تیز ہے۔\n\n### کیا اسر کا روزہ کافی ہے؟\n\nIsar موبائل کے لیے تیز ترین ڈیٹا بیس میں سے ایک ہے، اس لیے اسے زیادہ تر استعمال کے معاملات کے لیے کافی تیز ہونا چاہیے۔ اگر آپ کارکردگی کے مسائل کا شکار ہیں، تو امکان یہ ہے کہ آپ کچھ غلط کر رہے ہیں۔\n\n### کیا اسر میری ایپ کا سائز بڑھاتا ہے؟\n\nA little bit, yes. Isar will increase the download size of your app by about 1 - 1.5 MB. Isar Web adds only a few KB.\n\n### دستاویزات غلط ہیں / ٹائپنگ کی غلطی ہے۔\n\nOh no, sorry. Please [open an issue](https://github.com/isar-community/isar/issues/new/choose) or, even better, a PR to fix it 💪.\n"
  },
  {
    "path": "docs/docs/ur/indexes.md",
    "content": "---\ntitle: انڈیکسز\n---\n\n# اشاریہ جات\n\nاشاریہ جات اسار کی سب سے طاقتور خصوصیت ہیں۔ بہت سے ایمبیڈڈ ڈیٹا بیس \"نارمل\" اشاریہ جات پیش کرتے ہیں (اگر بالکل بھی ہیں)، لیکن اسر کے پاس جامع اور ملٹی انٹری انڈیکس بھی ہوتے ہیں۔ یہ سمجھنا کہ اشاریہ جات کیسے کام کرتے ہیں استفسار کی کارکردگی کو بہتر بنانے کے لیے ضروری ہے۔ ای زار آپ کو یہ منتخب کرنے دیتا ہے کہ آپ کون سا انڈیکس استعمال کرنا چاہتے ہیں اور آپ اسے کیسے استعمال کرنا چاہتے ہیں۔ ہم ایک فوری تعارف کے ساتھ شروع کریں گے کہ اشاریہ جات کیا ہیں۔\n\n## اشاریہ جات کیا ہیں؟\n\nجب کسی مجموعے کو انڈیکس نہیں کیا جاتا ہے تو، قطاروں کی ترتیب کو کسی بھی طرح سے آپٹمائز کیے گئے استفسار کے ذریعے واضح نہیں کیا جا سکتا ہے، اور اس لیے آپ کے استفسار کو اشیاء کے ذریعے لکیری طور پر تلاش کرنا پڑے گا۔ دوسرے لفظوں میں، استفسار کو حالات سے مماثل چیزوں کو تلاش کرنے کے لیے ہر شے کے ذریعے تلاش کرنا ہوگی۔ جیسا کہ آپ تصور کر سکتے ہیں، اس میں کچھ وقت لگ سکتا ہے۔ ہر ایک چیز کو تلاش کرنا زیادہ کارآمد نہیں ہے۔\n\nFor example, this `Product` collection is entirely unordered.\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n#### ڈیٹا:\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\nایک سوال جو تمام پروڈکٹس کو تلاش کرنے کی کوشش کرتا ہے جن کی قیمت €30 سے ​​زیادہ ہے تمام نو قطاروں میں تلاش کرنا پڑتی ہے۔ یہ نو قطاروں کے لیے کوئی مسئلہ نہیں ہے، لیکن یہ 100k قطاروں کے لیے ایک مسئلہ بن سکتا ہے۔\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\nاس استفسار کی کارکردگی کو بہتر بنانے کے لیے، ہم `قیمت` پراپرٹی کو انڈیکس کرتے ہیں۔ ایک انڈیکس ایک ترتیب شدہ تلاش کی میز کی طرح ہے:\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n#### تیار کردہ انڈیکس:\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\nاب، استفسار بہت تیزی سے عمل میں لایا جا سکتا ہے۔ ایگزیکیوٹر براہ راست آخری تین انڈیکس قطاروں میں جا سکتا ہے اور متعلقہ اشیاء کو ان کی آئی ڈی سے تلاش کر سکتا ہے۔\n\n### چھانٹنا\n\nایک اور عمدہ چیز: اشاریہ جات انتہائی تیز چھانٹ سکتے ہیں۔ ترتیب شدہ سوالات مہنگے ہوتے ہیں کیونکہ ڈیٹا بیس کو تمام نتائج کو ترتیب دینے سے پہلے میموری میں لوڈ کرنا ہوتا ہے۔ یہاں تک کہ اگر آپ آفسیٹ یا حد کی وضاحت کرتے ہیں، تو وہ چھانٹنے کے بعد لاگو ہوتے ہیں۔\n\nآئیے تصور کریں کہ ہم چار سب سے سستی مصنوعات تلاش کرنا چاہتے ہیں۔ ہم درج ذیل استفسار استعمال کر سکتے ہیں:\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\nاس مثال میں، ڈیٹا بیس کو تمام (!) اشیاء کو لوڈ کرنا ہوگا، قیمت کے لحاظ سے ترتیب دینا ہوگا، اور چار مصنوعات کو سب سے کم قیمت کے ساتھ واپس کرنا ہوگا۔\n\nجیسا کہ آپ شاید تصور کر سکتے ہیں، یہ پچھلے انڈیکس کے ساتھ بہت زیادہ مؤثر طریقے سے کیا جا سکتا ہے. ڈیٹا بیس انڈیکس کی پہلی چار قطاریں لیتا ہے اور متعلقہ اشیاء کو واپس کرتا ہے کیونکہ وہ پہلے سے ہی درست ترتیب میں ہیں۔\n\nانڈیکس کو ترتیب دینے کے لیے استعمال کرنے کے لیے، ہم استفسار کو اس طرح لکھیں گے:\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\nThe `.anyX()` where clause tells Isar to use an index just for sorting. You can also use a where clause like `.priceGreaterThan()` and get sorted results.\n\n## منفرد اشاریہ جات\n\nایک منفرد انڈیکس اس بات کو یقینی بناتا ہے کہ انڈیکس میں کوئی ڈپلیکیٹ قدر شامل نہیں ہے۔ یہ ایک یا متعدد خصوصیات پر مشتمل ہوسکتا ہے۔ اگر ایک منفرد انڈیکس میں ایک خاصیت ہے، تو اس خاصیت کی قدریں منفرد ہوں گی۔ اگر منفرد انڈیکس میں ایک سے زیادہ خاصیتیں ہیں، تو ان خصوصیات میں اقدار کا مجموعہ منفرد ہے۔\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\nمنفرد انڈیکس میں ڈیٹا داخل کرنے یا اپ ڈیٹ کرنے کی کوئی بھی کوشش جو ڈپلیکیٹ کا سبب بنتی ہے اس کے نتیجے میں ایک خرابی ہوگی:\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> ok\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// try to insert user with same username\nawait isar.users.put(user2); // -> error: unique constraint violated\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## اشاریہ جات کو تبدیل کریں۔\n\nاگر کسی انوکھی رکاوٹ کی خلاف ورزی کی جاتی ہے تو بعض اوقات غلطی کرنا بہتر نہیں ہوتا۔ اس کے بجائے، آپ موجودہ آبجیکٹ کو نئے سے تبدیل کرنا چاہیں گے۔ یہ انڈیکس کی 'ری پلیس' پراپرٹی کو 'سچ' پر سیٹ کر کے حاصل کیا جا سکتا ہے۔\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\nاب جب ہم موجودہ صارف نام کے ساتھ کسی صارف کو داخل کرنے کی کوشش کرتے ہیں، تو ای زار موجودہ صارف کو نئے صارف کے ساتھ بدل دے گا۔\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\nانڈیکس کو تبدیل کرنے سے `پٹ بائی()` طریقے بھی تیار ہوتے ہیں جو آپ کو اشیاء کو تبدیل کرنے کے بجائے اپ ڈیٹ کرنے کی اجازت دیتے ہیں۔ موجودہ آئی ڈی کو دوبارہ استعمال کیا جاتا ہے، اور لنکس اب بھی آباد ہیں۔\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// user does not exist so this is the same as put()\nawait isar.users.putByUsername(user1); \nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\nجیسا کہ آپ دیکھ سکتے ہیں، پہلے داخل کردہ صارف کی شناخت دوبارہ استعمال کی جاتی ہے۔\n\n## کیس غیر حساس اشاریہ جات\n\nAll indexes on `String` and `List<String>` properties are case-sensitive by default. If you want to create a case-insensitive index, you can use the `caseSensitive` option:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## انڈیکس کی قسم\n\nاشاریہ جات کی مختلف اقسام ہیں۔ زیادہ تر وقت، آپ ایک `IndexType.value` انڈیکس استعمال کرنا چاہیں گے، لیکن ہیش انڈیکس زیادہ موثر ہوتے ہیں۔\n\n### ویلیو انڈیکس\n\nویلیو انڈیکس ڈیفالٹ قسم ہیں اور ان تمام پراپرٹیز کے لیے صرف ایک کی اجازت ہے جس میں سٹرنگز یا فہرستیں نہیں ہیں۔ انڈیکس بنانے کے لیے پراپرٹی ویلیوز کا استعمال کیا جاتا ہے۔ فہرستوں کے معاملے میں، فہرست کے عناصر استعمال کیے جاتے ہیں۔ یہ تینوں انڈیکس اقسام میں سب سے زیادہ لچکدار ہے لیکن جگہ استعمال کرنے والا بھی ہے۔\n\n:::tip\nUse `IndexType.value` for primitives, Strings where you need `startsWith()` where clauses, and Lists if you want to search for individual elements.\n:::\n\n### ہیش انڈیکس\n\nانڈیکس کے لیے درکار اسٹوریج کو نمایاں طور پر کم کرنے کے لیے سٹرنگز اور لسٹوں کو ہیش کیا جا سکتا ہے۔ ہیش اشاریہ جات کا نقصان یہ ہے کہ انہیں سابقہ ​​اسکین کے لیے استعمال نہیں کیا جا سکتا (`startsWith` جہاں شقیں ہیں)۔\n\n:::tip\nUse `IndexType.hash` for Strings and Lists if you don't need `startsWith`, and `elementEqualTo` where clauses.\n:::\n\n### HashElements انڈیکس\n\nString lists can be hashed as a whole (using `IndexType.hash`), or the elements of the list can be hashed separately (using `IndexType.hashElements`), effectively creating a multi-entry index with hashed elements.\n\n:::tip\nUse `IndexType.hashElements` for `List<String>` where you need `elementEqualTo` where clauses.\n:::\n\n## جامع اشاریہ جات\n\nایک جامع اشاریہ متعدد خصوصیات پر مشتمل ایک اشاریہ ہے۔ ای زار آپ کو تین خصوصیات تک کے جامع اشاریہ جات بنانے کی اجازت دیتا ہے۔\n\nجامع اشاریہ جات کو متعدد کالم اشاریہ جات کے نام سے بھی جانا جاتا ہے۔\n\nشاید ایک مثال کے ساتھ شروع کرنا بہتر ہے۔ ہم ایک شخص کا مجموعہ بناتے ہیں اور عمر اور نام کی خصوصیات پر ایک جامع انڈیکس کی وضاحت کرتے ہیں:\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n#### Data:\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n#### تیار کردہ انڈیکس\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\nتیار کردہ کمپوزٹ انڈیکس میں تمام افراد کو ان کی عمر کے لحاظ سے ان کے نام سے ترتیب دیا گیا ہے۔\n\nجامع اشاریہ جات بہت اچھے ہیں اگر آپ ایک سے زیادہ خصوصیات کے لحاظ سے ترتیب دی گئی موثر سوالات تخلیق کرنا چاہتے ہیں۔ وہ متعدد خصوصیات کے ساتھ اعلی درجے کی جہاں شقوں کو بھی فعال کرتے ہیں:\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\nجامع انڈیکس کی آخری خاصیت بھی اس طرح کی شرائط کی حمایت کرتی ہے۔ `startsWith()` or `lessThan()`:\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## ملٹی انٹری انڈیکس\n\nIf you index a list using `IndexType.value`, Isar خود بخود ملٹی انٹری انڈیکس بنائے گا، اور فہرست میں موجود ہر آئٹم کو آبجیکٹ کی طرف انڈیکس کیا جاتا ہے۔ یہ تمام اقسام کی فہرستوں کے لیے کام کرتا ہے۔\n\nملٹی انٹری انڈیکس کے لیے عملی ایپلی کیشنز میں ٹیگز کی فہرست کو انڈیکس کرنا یا مکمل ٹیکسٹ انڈیکس بنانا شامل ہے۔\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` splits a string into words according to the [Unicode Annex #29](https://unicode.org/reports/tr29/) specification, so it works for almost all languages correctly.\n\n#### ڈیٹا:\n\n| id  | description                  | descriptionWords             |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\nEntries with duplicate words only appear once in the index.\n\n#### تیار کردہ انڈیکس\n\n| descriptionWords | id        |\n| ---------------- | --------- |\n| comfortable      | [1, 2]    |\n| blue             | 1         |\n| necktie          | 4         |\n| plain            | 3         |\n| pullover         | 2         |\n| red              | [2, 3, 4] |\n| super            | 4         |\n| t-shirt          | [1, 3]    |\n\nیہ انڈیکس اب سابقہ ​​(یا مساوات) کے لیے استعمال کیا جا سکتا ہے جہاں وضاحت کے انفرادی الفاظ کی شقیں ہیں۔\n\n:::tip\nInstead of storing the words directly, also consider using the result of a [phonectic algorithm](https://en.wikipedia.org/wiki/Phonetic_algorithm) like [Soundex](https://en.wikipedia.org/wiki/Soundex).\n:::\n"
  },
  {
    "path": "docs/docs/ur/limitations.md",
    "content": "# حدود \n\nجیسا کہ آپ جانتے ہیں، ای زار ورچوئل مشین کے ساتھ ساتھ ویب پر چلنے والے موبائل آلات اور ڈیسک ٹاپس پر کام کرتا ہے۔ دونوں پلیٹ فارم بہت مختلف ہیں اور مختلف حدود ہیں۔\n\n## وی ایم حدود\n\n- کسی سٹرنگ کے صرف پہلے 1024 بائٹس کو ایک سابقہ ​​جہاں-شق کے لیے استعمال کیا جا سکتا ہے۔\n- اشیاء صرف 16MB سائز کی ہو سکتی ہیں۔\n\n## ویب کی حدود\n\nچونکہ  ای زار ویب انڈیکس دیٹا بیس پر انحصار کرتا ہے، اس لیے مزید حدود ہیں لیکن ای زار استعمال کرتے وقت وہ بمشکل ہی قابل توجہ ہیں۔\n\n- Synchronous methods are unsupported\n- Currently, `Isar.splitWords()` and `.matches()` filters are not yet implemented\n- Schema changes are not as tighly checked as in the VM so be careful to comply with the rules\n- All number types are stored as double (the only js number type) so `@Size32` has no effect\n- Indexes are represented differenlty so hash indexes don't use less space (they still work the same)\n- `col.delete()` and `col.deleteAll()` work correctly but the return value is not correct\n- `col.clear()` do not reset the auto-increment value\n- `NaN` is not supported as a value\n"
  },
  {
    "path": "docs/docs/ur/links.md",
    "content": "---\ntitle: لنکس\n---\n\n# لنکس\n\nروابط آپ کو اشیاء کے درمیان تعلقات کا اظہار کرنے کی اجازت دیتے ہیں، جیسے کہ تبصرہ کا مصنف (صارف)۔ آپ ای زار لنکس کے ساتھ `1:1`، `1:n`، اور `n:n` تعلقات کو ماڈل بنا سکتے ہیں۔ لنکس کا استعمال ایمبیڈڈ اشیاء کے استعمال سے کم ایرگونومک ہے اور جب بھی ممکن ہو آپ کو ایمبیڈڈ اشیاء کا استعمال کرنا چاہیے۔\n\nلنک کو ایک علیحدہ جدول کے طور پر سوچیں جس میں رشتہ موجود ہو۔ یہ ایس کیو ایل ریلیشنز کی طرح ہے لیکن اس میں ایک مختلف فیچر سیٹ اور اےپی آئی ہے۔\n\n## IsarLink\n\n`IsarLink<T>` can contain no or one related object, and it can be used to express a to-one relationship. `IsarLink` has a single property called `value` which holds the linked object.\n\nLinks are lazy, so you need to tell the `IsarLink` to load or save the `value` explicitly. You can do this by calling `linkProperty.load()` and `linkProperty.save()`.\n\n:::tip\nکسی لنک کے سورس اور ٹارگٹ کلیکشن کی آئی ڈی پراپرٹی غیر حتمی ہونی چاہیے۔\n:::\n\nغیر ویب اہداف کے لیے، جب آپ انہیں پہلی بار استعمال کرتے ہیں تو لنکس خود بخود لوڈ ہو جاتے ہیں۔ آئیے ایک مجموعہ میں ایک IsarLink شامل کرکے شروع کریں:\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\nہم نے اساتذہ اور طلباء کے درمیان ایک ربط کی وضاحت کی۔ اس مثال میں ہر طالب علم کو بالکل ایک استاد ہو سکتا ہے۔\n\nسب سے پہلے، ہم استاد بناتے ہیں اور اسے ایک طالب علم کو تفویض کرتے ہیں۔ ہمیں استاد کو `.پٹ()` کرنا ہوگا اور لنک کو دستی طور پر محفوظ کرنا ہوگا۔\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teachers.save();\n});\n```\n\nWe can now use the link:\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\nLet's try the same thing with synchronous code. We don't need to save the link manually because `.putSync()` automatically saves all links. It even creates the teacher for us.\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\nیہ زیادہ معنی خیز ہوگا اگر پچھلی مثال کے طالب علم کے متعدد اساتذہ ہوسکتے ہیں۔ Fortunately, Isar has `IsarLinks<T>`, which can contain multiple related objects and express a to-many relationship.\n\n`IsarLinks<T>` extends `Set<T>` and exposes all the methods that are allowed for sets.\n\n`IsarLinks` behaves much like `IsarLink` and is also lazy. To load all linked object call `linkProperty.load()`. To persist the changes, call `linkProperty.save()`.\n\nInternally both `IsarLink` and `IsarLinks` are represented in the same way. We can upgrade the `IsarLink<Teacher>` from before to an `IsarLinks<Teacher>` to assign multiple teachers to a single student (without losing data).\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLinks<Teacher>();\n}\n```\n\nThis works because we did not change the name of the link (`teacher`), so Isar remembers it from before.\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## بیک لنکس\n\nمیں نے آپ کو یہ پوچھتے ہوئے سنا ہے، \"اگر ہم معکوس تعلقات کا اظہار کرنا چاہتے ہیں تو کیا ہوگا؟\"۔ فکر مت کرو؛ اب ہم بیک لنکس متعارف کرائیں گے۔\n\nBacklinks are links in the reverse direction. Each link always has an implicit backlink. You can make it available to your app by annotating an `IsarLink` or `IsarLinks` with `@Backlink()`.\n\nبیک لنکس کو اضافی میموری یا وسائل کی ضرورت نہیں ہوتی ہے۔ آپ ڈیٹا کو کھونے کے بغیر انہیں آزادانہ طور پر شامل، ہٹا سکتے اور ان کا نام تبدیل کر سکتے ہیں۔\n\nہم یہ جاننا چاہتے ہیں کہ ایک مخصوص استاد کون سے طلباء کے پاس ہے، اس لیے ہم ایک بیک لنک کی وضاحت کرتے ہیں:\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\nہمیں اس لنک کی وضاحت کرنے کی ضرورت ہے جس کی طرف بیک لنک اشارہ کرتا ہے۔ دو اشیاء کے درمیان متعدد مختلف روابط کا ہونا ممکن ہے۔\n\n## لنکس شروع کریں۔\n\n`IsarLink` and `IsarLinks` have a zero-arg constructor, which should be used to assign the link property when the object is created. It is good practice to make link properties `final`.\n\nجب آپ پہلی بار اپنے آبجیکٹ کو `پٹ()` کرتے ہیں، تو لنک سورس اور ٹارگٹ کلیکشن کے ساتھ شروع ہو جاتا ہے، اور آپ `لوڈ()` اور `سیو()` جیسے طریقوں کو کال کر سکتے ہیں۔ ایک لنک اپنی تخلیق کے فوراً بعد تبدیلیوں کو ٹریک کرنا شروع کر دیتا ہے، لہذا آپ لنک شروع ہونے سے پہلے ہی تعلقات کو شامل اور ہٹا سکتے ہیں۔\n\n:::danger\nکسی لنک کو کسی دوسری چیز میں منتقل کرنا غیر قانونی ہے۔\n:::\n"
  },
  {
    "path": "docs/docs/ur/queries.md",
    "content": "---\ntitle: سوالات\n---\n\n# سوالات\n\nاستفسار یہ ہے کہ آپ کو ایسے ریکارڈز کیسے ملتے ہیں جو کچھ شرائط سے میل کھاتے ہیں، مثال کے طور پر:\n\n- تمام ستارے والے رابطے تلاش کریں۔\n- رابطوں میں الگ الگ نام تلاش کریں۔\n- ان تمام رابطوں کو حذف کریں جن کے آخری نام کی وضاحت نہیں کی گئی ہے۔\n\nچونکہ سوالات ڈیٹا بیس پر کیے جاتے ہیں نہ کہ ڈارٹ میں، وہ واقعی تیز ہیں۔ جب آپ ہوشیاری سے اشاریہ جات کا استعمال کرتے ہیں، تو آپ استفسار کی کارکردگی کو مزید بہتر بنا سکتے ہیں۔ مندرجہ ذیل میں، آپ سیکھیں گے کہ سوالات کیسے لکھتے ہیں اور آپ انہیں جتنی جلدی ممکن ہو سکے کیسے بنا سکتے ہیں۔\n\nآپ کے ریکارڈ کو فلٹر کرنے کے دو مختلف طریقے ہیں: فلٹرز اور جہاں شقیں۔ ہم ایک نظر ڈال کر شروع کریں گے کہ فلٹرز کیسے کام کرتے ہیں۔\n\n## فلٹرز\n\nفلٹرز استعمال کرنے اور سمجھنے میں آسان ہیں۔ آپ کی خصوصیات کی قسم پر منحصر ہے، مختلف فلٹر آپریشنز دستیاب ہیں جن میں سے اکثر کے خود وضاحتی نام ہیں۔\n\nفلٹر فلٹر کیے جانے والے مجموعہ میں موجود ہر شے کے اظہار کا جائزہ لے کر کام کرتے ہیں۔ اگر اظہار 'سچ' پر حل کرتا ہے، تو اسر نتائج میں اعتراض کو شامل کرتا ہے۔ فلٹرز نتائج کی ترتیب کو متاثر نہیں کرتے ہیں۔\n\nہم ذیل کی مثالوں کے لیے درج ذیل ماڈل استعمال کریں گے۔\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### استفسار کی شرائط\n\nفیلڈ کی قسم پر منحصر ہے، مختلف شرائط دستیاب ہیں۔\n\n| Condition | Description |\n| ----------| ------------|\n| `.equalTo(value)` | Matches values that are equal to the specified `value`. |\n| `.between(lower, upper)` | Matches values that are between `lower` and `upper`. |\n| `.greaterThan(bound)` | Matches values that are greater than `bound`. |\n| `.lessThan(bound)` | Matches values that are less than `bound`. `null` values will be included by default because `null` is considered smaller than any other value. |\n| `.isNull()` | Matches values that are `null`.|\n| `.isNotNull()` | Matches values that are not `null`.|\n| `.length()` | List, String and link length queries filter objects based on the number of elements in a list or link. |\n\nLet's assume the database contains four shoes with sizes 39, 40, 46 and one with an un-set (`null`) size. Unless you perform sorting, the values will be returned sorted by id.\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### منطقی آپریٹرز\n\nآپ مندرجہ ذیل منطقی آپریٹرز کا استعمال کرتے ہوئے جامع پیشن گوئی کر سکتے ہیں:\n\n| Operator   | Description |\n| ---------- | ----------- |\n| `.and()`   | Evaluates to `true` if both left-hand and right-hand expressions evaluate to `true`. |\n| `.or()`    | Evaluates to `true` if either expression evaluates to `true`. |\n| `.xor()`   | Evaluates to `true` if exactly one expression evaluates to `true`. |\n| `.not()`   | Negates the result of the following expression. |\n| `.group()` | Group conditions and allow to specify order of evaluation. |\n\nاگر آپ 46 سائز میں تمام جوتے تلاش کرنا چاہتے ہیں، تو آپ درج ذیل استفسار کا استعمال کر سکتے ہیں:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\nIf you want to use more than one condition, you can combine multiple filters using logical **and** `.and()`, logical **or** `.or()` and logical **xor** `.xor()`.\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // Optional. Filters are implicitly combined with logical and.\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\nThis query is equivalent to: `size == 46 && isUnisex == true`.\n\nYou can also group conditions using `.group()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\nThis query is equivalent to `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`.\n\nTo negate a condition or group, use logical **not** `.not()`:\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\nThis query is equivalent to `size != 46 && isUnisex != true`.\n\n### سٹرنگ کے حالات\n\nمندرجہ بالا استفسار کی شرائط کے علاوہ، سٹرنگ اقدار کچھ اور شرائط پیش کرتی ہیں جنہیں آپ استعمال کر سکتے ہیں۔ مثال کے طور پر ریجیکس جیسے وائلڈ کارڈز تلاش میں مزید لچک پیدا کرتے ہیں۔\n\n| Condition            | Description                                                       |\n| -------------------- | ----------------------------------------------------------------- |\n| `.startsWith(value)` | Matches string values that begins with provided `value`.          |\n| `.contains(value)`   | Matches string values that contain the provided `value`.          |\n| `.endsWith(value)`   | Matches string values that end with the provided `value`.         |\n| `.matches(wildcard)` | Matches string values that match the provided `wildcard` pattern. |\n\n**کیس کی حساسیت**  \nAll string operations have an optional `caseSensitive` parameter that defaults to `true`.\n\n**Wildcards:**  \nA [wildcard string expression](https://en.wikipedia.org/wiki/Wildcard_character) is a string that uses normal characters with two special wildcard characters:\n\n- The `*` wildcard matches zero or more of any character\n- The `?` wildcard matches any character.\n  For example, the wildcard string `\"d?g\"` matches `\"dog\"`, `\"dig\"`, and `\"dug\"`, but not `\"ding\"`, `\"dg\"`, or `\"a dog\"`.\n\n### سوال میں ترمیم کرنے والے\n\nبعض اوقات کچھ شرائط یا مختلف اقدار کی بنیاد پر استفسار کرنا ضروری ہوتا ہے۔ aای زار مشروط سوالات کی تعمیر کے لئے ایک بہت طاقتور ٹول ہے:\n\n| Modifier              | Description                                          |\n| --------------------- | ---------------------------------------------------- |\n| `.optional(cond, qb)` | Extends the query only if the `condition` is `true`. This can be used almost anywhere in a query for example to conditionally sort or limit it. |\n| `.anyOf(list, qb)`    | Extends the query for each value in `values` and combines the conditions using logical **or**. |\n| `.allOf(list, qb)`    | Extends the query for each value in `values` and combines the conditions using logical **and**. |\n\nIn this example, we build a method that can find shoes with an optional filter:\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // only apply filter if sizeFilter != null\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\nIf you want to find all shoes that have one of multiple shoe sizes, you can either write a conventional query or use the `anyOf()` modifier:\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\nاستفسار میں ترمیم کرنے والے خاص طور پر اس وقت مفید ہوتے ہیں جب آپ متحرک سوالات بنانا چاہتے ہیں۔\n\n### فہرستیں\n\nیہاں تک کہ فہرستوں سے بھی استفسار کیا جا سکتا ہے:\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\nآپ فہرست کی لمبائی کی بنیاد پر استفسار کر سکتے ہیں:\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\nThese are equivalent to the Dart code `tweets.where((t) => t.hashtags.isEmpty);` and `tweets.where((t) => t.hashtags.length > 5);`. You can also query based on list elements:\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\nThis is equivalent to the Dart code `tweets.where((t) => t.hashtags.contains('flutter'));`.\n\n### ایمبیڈڈ اشیاء\n\nایمبیڈڈ اشیاء اسر کی سب سے مفید خصوصیات میں سے ایک ہیں۔ اعلیٰ سطحی اشیاء کے لیے دستیاب انہی شرائط کا استعمال کرتے ہوئے ان سے بہت مؤثر طریقے سے استفسار کیا جا سکتا ہے۔ آئیے فرض کریں کہ ہمارے پاس مندرجہ ذیل ماڈل ہے:\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\nWe want to query all cars that have a brand with the name `\"BMW\"` and the country `\"Germany\"`. We can do this using the following query:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\nہمیشہ نیسٹڈ سوالات کو گروپ کرنے کی کوشش کریں۔ مندرجہ بالا استفسار درج ذیل سے زیادہ موثر ہے۔ اگرچہ نتیجہ ایک ہی ہے:\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### لنکس\n\nاگر آپ کے ماڈل میں [لنک یا بیک لنکس](لنک) ہیں تو آپ لنک شدہ اشیاء یا منسلک اشیاء کی تعداد کی بنیاد پر اپنے استفسار کو فلٹر کر سکتے ہیں۔\n\n:::warning\nذہن میں رکھیں کہ لنک کے سوالات مہنگے ہوسکتے ہیں کیونکہ اسر کو منسلک اشیاء کو تلاش کرنے کی ضرورت ہے۔ اس کے بجائے سرایت شدہ اشیاء استعمال کرنے پر غور کریں۔\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\nہم ان تمام طلباء کو تلاش کرنا چاہتے ہیں جن کے پاس ریاضی یا انگریزی کے استاد ہیں:\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\nLink filters evaluate to `true` if at least one linked object matches the conditions.\n\nLet's search for all students that have no teachers:\n  \n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\nor alternatively:\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## جہاں کلازز\n\nجہاں شقیں ایک بہت طاقتور ٹول ہیں، لیکن انہیں درست کرنا تھوڑا مشکل ہو سکتا ہے۔\n\nفلٹرز کے برعکس جہاں شقیں استفسار کے حالات کو چیک کرنے کے لیے اسکیما میں بیان کردہ اشاریہ جات کا استعمال کرتی ہیں۔ انڈیکس سے استفسار کرنا ہر ریکارڈ کو انفرادی طور پر فلٹر کرنے سے کہیں زیادہ تیز ہے۔\n\n➡️ Learn more: [Indexes](indexes)\n\n:::tip\nایک بنیادی اصول کے طور پر، آپ کو ہمیشہ جہاں تک ممکن ہو وہاں کی شقوں کا استعمال کرتے ہوئے ریکارڈ کو کم کرنے کی کوشش کرنی چاہیے اور باقی فلٹرنگ فلٹرز کا استعمال کرتے ہوئے کرنی چاہیے۔\n:::\n\nآپ صرف منطقی **یا** کا استعمال کرتے ہوئے شقوں کو جوڑ سکتے ہیں۔ دوسرے الفاظ میں، آپ متعدد جہاں شقوں کو ایک ساتھ جمع کر سکتے ہیں، لیکن آپ متعدد جہاں شقوں کے تقاطع سے استفسار نہیں کر سکتے۔\n\nآئیے جوتوں کے مجموعہ میں اشاریہ جات شامل کریں:\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\nThere are two indexes. The index on `size` allows us to use where clauses like `.sizeEqualTo()`. The composite index on `isUnisex` allows where clauses like `isUnisexSizeEqualTo()`. But also `isUnisexEqualTo()` because you can always use any prefix of an index.\n\nاب ہم کمپوزٹ انڈیکس کا استعمال کرتے ہوئے 46 سائز میں یونیسیکس جوتے تلاش کرنے سے پہلے کے سوال کو دوبارہ لکھ سکتے ہیں۔ یہ استفسار پچھلی کی نسبت بہت تیز ہوگا:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nWhere clauses have two more superpowers: They give you \"free\" sorting and a super fast distinct operation.\n\n### جہاں شقوں اور فلٹرز کو ملانا\n\nRemember the `shoes.filter()` queries? It's actually just a shortcut for `shoes.where().filter()`. You can (and should) combine where clauses and filters in the same query to use the benefits of both:\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\nجہاں شق پہلے لاگو کی جاتی ہے تاکہ فلٹر کیے جانے والے آبجیکٹ کی تعداد کو کم کیا جا سکے۔ پھر فلٹر بقیہ آبجیکٹس پر لاگو ہوتا ہے۔\n\n## چھانٹنا\nYou can define how the results should be sorted when executing the query using the `.sortBy()`, `.sortByDesc()`, `.thenBy()` and `.thenByDesc()` methods.\n\nانڈیکس کا استعمال کیے بغیر تمام جوتوں کو ماڈل کے نام کے مطابق صعودی ترتیب اور نزولی ترتیب میں سائز تلاش کرنے کے لیے:\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\nبہت سے نتائج کو ترتیب دینا مہنگا ہو سکتا ہے، خاص طور پر چونکہ چھانٹنا آفسیٹ اور حد سے پہلے ہوتا ہے۔ اوپر چھانٹنے کے طریقے کبھی بھی اشاریہ جات کا استعمال نہیں کرتے ہیں۔ خوش قسمتی سے، ہم دوبارہ استعمال کر سکتے ہیں جہاں شق چھانٹنا ہے اور اپنی استفسار کو تیز رفتار بنا سکتے ہیں چاہے ہمیں ایک ملین اشیاء کو ترتیب دینے کی ضرورت ہو۔\n\n### جہاں شق کی چھانٹی\n\nاگر آپ اپنی استفسار میں ایک **سنگل** جہاں شق استعمال کرتے ہیں، تو نتائج پہلے ہی انڈیکس کے مطابق ترتیب دیئے گئے ہیں۔ یہ ایک بڑی بات ہے!\n\nLet's assume we have shoes in sizes `[43, 39, 48, 40, 42, 45]` and we want to find all shoes with a size greater than `42` and also have them sorted by size:\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // also sorts the results by size\n  .findAll(); // -> [43, 45, 48]\n```\n\nAs you can see, the result is sorted by the `size` index. If you want to reverse the where clause sort order, you can set `sort` to `Sort.desc`:\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\nبعض اوقات آپ جہاں کی شق استعمال نہیں کرنا چاہتے لیکن پھر بھی مضمر چھانٹی سے فائدہ اٹھاتے ہیں۔ آپ 'کوئی' استعمال کر سکتے ہیں جہاں شق:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\nاگر آپ ایک جامع اشاریہ استعمال کرتے ہیں، تو نتائج کو اشاریہ کے تمام شعبوں کے حساب سے ترتیب دیا جاتا ہے۔\n\n:::tip\nIf you need the results to be sorted, consider using an index for that purpose. Especially if you work with `offset()` and `limit()`.\n:::\n\nبعض اوقات چھانٹنے کے لیے اشاریہ استعمال کرنا ممکن یا مفید نہیں ہوتا ہے۔ اس طرح کے معاملات کے لیے، آپ کو انڈیکس کا استعمال کرنا چاہیے تاکہ نتیجے میں آنے والے اندراجات کی تعداد کو جتنا ممکن ہو کم کیا جا سکے۔\n\n## منفرد اقدار\n\nمنفرد قدروں کے ساتھ صرف اندراجات واپس کرنے کے لیے، الگ پیش گوئی کا استعمال کریں۔ مثال کے طور پر، یہ معلوم کرنے کے لیے کہ آپ کے ای زار ڈیٹا بیس میں آپ کے جوتوں کے کتنے مختلف ماڈل ہیں:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\nآپ الگ الگ ماڈل سائز کے امتزاج کے ساتھ تمام جوتوں کو تلاش کرنے کے لیے متعدد مختلف شرائط کو بھی جوڑ سکتے ہیں:\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\nہر ایک الگ امتزاج کا صرف پہلا نتیجہ واپس آتا ہے۔ آپ اسے کنٹرول کرنے کے لیے جہاں کی شقیں اور چھانٹنے کی کارروائیاں استعمال کر سکتے ہیں۔\n\n### جہاں شق الگ ہے۔\n\nاگر آپ کے پاس غیر منفرد انڈیکس ہے، تو آپ اس کی تمام الگ الگ اقدار حاصل کرنا چاہتے ہیں۔ آپ پچھلے حصے سے `distinctBy` آپریشن استعمال کر سکتے ہیں، لیکن یہ چھانٹنے اور فلٹر کرنے کے بعد انجام دیا جاتا ہے، اس لیے کچھ اوور ہیڈ ہے۔\nاگر آپ صرف ایک جہاں کی شق استعمال کرتے ہیں، تو آپ اس کے بجائے الگ آپریشن کرنے کے لیے انڈیکس پر انحصار کر سکتے ہیں۔\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\nنظریہ میں، آپ یہاں تک کہ ایک سے زیادہ استعمال کر سکتے ہیں جہاں چھانٹنے اور الگ کرنے کے لیے شقیں ہیں۔ پابندی صرف یہ ہے کہ جہاں شقیں اوور لیپنگ نہ ہوں اور وہی انڈیکس استعمال کریں۔ درست چھانٹنے کے لیے، انہیں بھی ترتیب کے لحاظ سے لاگو کرنے کی ضرورت ہے۔ اگر آپ اس پر بھروسہ کرتے ہیں تو بہت محتاط رہیں!\n:::\n\n## آفسیٹ اور حد\n\nسست فہرست کے نظارے کے لیے استفسار کے نتائج کی تعداد کو محدود کرنا اکثر اچھا خیال ہوتا ہے۔ آپ ایک `لیمٹ()` سیٹ کر کے ایسا کر سکتے ہیں:\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\nBy setting an `offset()` you can also paginate the results of your query.\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\nچونکہ ڈارٹ آبجیکٹ کو انسٹیٹیوٹ کرنا اکثر استفسار پر عمل کرنے کا سب سے مہنگا حصہ ہوتا ہے، اس لیے یہ ایک اچھا خیال ہے کہ آپ کو مطلوبہ اشیاء کو لوڈ کیا جائے۔\n\n## ایگزیکیوشن آرڈر\nای زار سوالات کو ہمیشہ اسی ترتیب میں انجام دیتا ہے:\n\n1. اشیاء تلاش کرنے کے لیے پرائمری یا سیکنڈری انڈیکس کو عبور کریں (جہاں شقیں لگائیں)\n2. اشیاء کو فلٹر کریں۔\n3. نتائج ترتیب دیں۔\n4. الگ آپریشن کا اطلاق کریں۔\n5. آف سیٹ اور نتائج کو محدود کریں۔\n6. نتائج واپس کریں۔\n\n## استفسار کے آپریشنز\n\nIn the previous examples, we used `.findAll()` to retrieve all matching objects. There are more operations available, however:\n\n| Operation        | Description                                                                                                         |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | Retreive only the first matching object or `null` if none matches.                                                  |\n| `.findAll()`     | Retreive all matching objects.                                                                                      |\n| `.count()`       | Count how many objects match the query.                                                                             |\n| `.deleteFirst()` | Delete the first matching object from the collection.                                                               |\n| `.deleteAll()`   | Delete all matching objects from the collection.                                                                    |\n| `.build()`       | Compile the query to reuse it later. This saves the cost to build a query if you want to execute it multiple times. |\n\n## پراپرٹی کے سوالات\n\nاگر آپ صرف ایک پراپرٹی کی قدروں میں دلچسپی رکھتے ہیں، تو آپ پراپرٹی استفسار استعمال کرسکتے ہیں۔ بس ایک باقاعدہ استفسار بنائیں اور ایک پراپرٹی منتخب کریں:\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\nصرف ایک پراپرٹی کا استعمال ڈی سیریلائزیشن کے دوران وقت بچاتا ہے۔ پراپرٹی کے سوالات ایمبیڈڈ اشیاء اور فہرستوں کے لیے بھی کام کرتے ہیں۔\n\n## جمع کرنا\n\nاسار پراپرٹی کے سوال کی قدروں کو جمع کرنے کی حمایت کرتا ہے۔ مندرجہ ذیل جمع آپریشن دستیاب ہیں:\n\n| Operation    | Description                                                    |\n| ------------ | -------------------------------------------------------------- |\n| `.min()`     | Finds the minimum value or `null` if none matches.             |\n| `.max()`     | Finds the maximum value or `null` if none matches.             |\n| `.sum()`     | Sums all values.                                               |\n| `.average()` | Calculates the average of all values or `NaN` if none matches. |\n\nجمع کا استعمال تمام مماثل اشیاء کو تلاش کرنے اور دستی طور پر جمع کرنے سے کہیں زیادہ تیز ہے۔\n\n## متحرک سوالات\n\n:::danger\nیہ سیکشن غالباً آپ سے متعلق نہیں ہے۔ متحرک سوالات استعمال کرنے کی حوصلہ شکنی کی جاتی ہے جب تک کہ آپ کو بالکل ضرورت نہ ہو (اور آپ شاذ و نادر ہی کرتے ہیں)۔\n:::\n\nAll the examples above used the QueryBuilder and the generated static extension methods. Maybe you want to create dynamic queries or a custom query language (like the Isar Inspector). In that case, you can use the `buildQuery()` method:\n\n| Parameter       | Description                                                                                 |\n| --------------- | ------------------------------------------------------------------------------------------- |\n| `whereClauses`  | The where clauses of the query.                                                             |\n| `whereDistinct` | Whether where clauses should return distinct values (only useful for single where clauses). |\n| `whereSort`     | The traverse order of the where clauses (only useful for single where clauses).             |\n| `filter`        | The filter to apply to the results.                                                         |\n| `sortBy`        | A list of properties to sort by.                                                            |\n| `distinctBy`    | A list of properties to distinct by.                                                        |\n| `offset`        | The offset of the results.                                                                  |\n| `limit`         | The maximum number of results to return.                                                    |\n| `property`      | If non-null, only the values of this property are returned.                                 |\n\nآئیے ایک متحرک استفسار بنائیں:\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\nدرج ذیل استفسار مساوی ہے:\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/ur/recipes/data_migration.md",
    "content": "---\n\ntitle: ڈیٹا مائیگریشن\n---\n\n# ڈیٹا مائیگریشن\n\nاگر آپ مجموعے، فیلڈز، یا اشاریہ جات کو شامل یا ہٹاتے ہیں تو ایزار خود بخود آپ کے ڈیٹا بیس اسکیموں کو منتقل کر دیتا ہے۔ بعض اوقات آپ اپنے ڈیٹا کو بھی منتقل کرنا چاہتے ہیں۔ ایزار ایک بلٹ ان حل پیش نہیں کرتا ہے کیونکہ یہ من مانی نقل مکانی پر پابندیاں عائد کرے گا۔ ہجرت کی منطق کو لاگو کرنا آسان ہے جو آپ کی ضروریات کے مطابق ہو۔\n\nہم اس مثال میں پورے ڈیٹا بیس کے لیے ایک ہی ورژن استعمال کرنا چاہتے ہیں۔ ہم موجودہ ورژن کو ذخیرہ کرنے کے لیے مشترکہ ترجیحات کا استعمال کرتے ہیں اور اس کا موازنہ اس ورژن سے کرتے ہیں جس میں ہم منتقل ہونا چاہتے ہیں۔ اگر ورژن مماثل نہیں ہیں، تو ہم ڈیٹا کو منتقل کرتے ہیں اور ورژن کو اپ ڈیٹ کرتے ہیں۔\n\n\n::: warning\nآپ ہر مجموعہ کو اس کا اپنا ورژن بھی دے سکتے ہیں اور انہیں انفرادی طور پر منتقل کر سکتے ہیں۔\n:::\n\nتصور کریں کہ ہمارے پاس سالگرہ والے فیلڈ کے ساتھ صارف کا مجموعہ ہے۔ ہماری ایپ کے ورژن 2 میں، ہمیں عمر کی بنیاد پر صارفین سے استفسار کرنے کے لیے ایک اضافی پیدائشی سال کی فیلڈ کی ضرورت ہے۔\n\nVersion 1:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\nVersion 2:\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\nمسئلہ یہ ہے کہ موجودہ صارف کے ماڈلز میں خالی `پیدائشی سال` فیلڈ ہوگی کیونکہ یہ ورژن 1 میں موجود نہیں تھا۔ ہمیں `پیدائشی سال` فیلڈ سیٹ کرنے کے لئے ڈیٹا کو منتقل کرنے کی ضرورت ہے۔\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // If the version is not set (new installation) or already 2, we do not need to migrate\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // Update version\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // We paginate through the users to avoid loading all users into memory at once\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // We don't need to update anything since the birthYear getter is used\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\nاگر آپ کو بہت سارے ڈیٹا کو منتقل کرنا ہے تو، یوآئی تھریڈ پر دباؤ کو روکنے کے لیے بیک گراؤنڈ آئسولیٹ استعمال کرنے پر غور کریں۔\n:::\n"
  },
  {
    "path": "docs/docs/ur/recipes/full_text_search.md",
    "content": "---\ntitle: مکمل متن کی تلاش\n---\n\n# مکمل متن کی تلاش\n\nمکمل متن کی تلاش ڈیٹا بیس میں متن تلاش کرنے کا ایک طاقتور طریقہ ہے۔ آپ کو پہلے سے ہی واقف ہونا چاہئے کہ [اشاریہ جات](/اشاریہ جات) کیسے کام کرتے ہیں، لیکن آئیے بنیادی باتوں کو دیکھتے ہیں۔\n\nایک انڈیکس تلاش کی میز کی طرح کام کرتا ہے، جس سے استفسار کے انجن کو دی گئی قدر کے ساتھ تیزی سے ریکارڈ تلاش کرنے کی اجازت ملتی ہے۔ مثال کے طور پر، اگر آپ کے آبجیکٹ میں ایک `ٹائٹل` فیلڈ ہے، تو آپ اس فیلڈ پر ایک انڈیکس بنا سکتے ہیں تاکہ دیے گئے عنوان کے ساتھ اشیاء کو تلاش کرنا تیز تر بنایا جا سکے۔\n\n## مکمل متن کی تلاش کیوں مفید ہے؟\n\nYou can easily search text using filters. There are various string operations for example `.startsWith()`, `.contains()` and `.matches()`. The problem with filters is that their runtime is `O(n)` where `n` is the number of records in the collection. String operations like `.matches()` are especially expensive.\n\n:::tip\nمکمل متن کی تلاش فلٹرز سے کہیں زیادہ تیز ہے، لیکن اشاریہ جات کی کچھ حدود ہیں۔ اس نسخہ میں، ہم ان حدود کے ارد گرد کام کرنے کا طریقہ دریافت کریں گے۔\n:::\n\n## بنیادی مثال\n\nخیال ہمیشہ ایک جیسا ہوتا ہے: پورے متن کو ترتیب دینے کے بجائے، ہم متن میں الفاظ کو ترتیب دیتے ہیں تاکہ ہم انفرادی طور پر ان کو تلاش کر سکیں۔\n\nآئیے سب سے بنیادی فل ٹیکسٹ انڈیکس بنائیں:\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\nWe can now search for messages with specific words in the content:\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\nیہ استفسار بہت تیز ہے، لیکن کچھ مسائل ہیں:\n\n1. ہم صرف پورے الفاظ تلاش کر سکتے ہیں۔\n2. ہم اوقاف پر غور نہیں کرتے\n3. ہم دوسرے وائٹ اسپیس حروف کی حمایت نہیں کرتے ہیں۔\n\n##متن کو صحیح طریقے سے تقسیم کرنا\n\nآئیے پچھلی مثال کو بہتر بنانے کی کوشش کرتے ہیں۔ ہم الفاظ کی تقسیم کو ٹھیک کرنے کے لیے ایک پیچیدہ ریجیکس تیار کرنے کی کوشش کر سکتے ہیں، لیکن یہ ممکنہ طور پر کنارے کے معاملات کے لیے سست اور غلط ہوگا۔\n\nThe [Unicode Annex #29](https://unicode.org/reports/tr29/) defines how to split text into words correctly for almost all languages. It is quite complicated, but fortunately, Isar does the heavy lifting for us:\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## میں مزید کنٹرول چاہتا ہوں۔\n\nبالکل آسان! ہم اپنے انڈیکس کو بھی تبدیل کر سکتے ہیں تاکہ سابقہ ​​مماثلت اور کیس غیر حساس مماثلت کو سپورٹ کیا جا سکے۔\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\nBy default, Isar will store the words as hashed values which is fast and space efficient. But hashes can't be used for prefix matching. Using `IndexType.value`, we can change the index to use the words directly instead. It gives us the `.titleWordsAnyStartsWith()` where clause:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## I also need `.endsWith()`\n\nSure thing! We will use a trick to achieve `.endsWith()` matching:\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\nDon't forget reversing the ending you want to search for:\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## Stemming algorithms\n\nUnfortunately, indexes do not support `.contains()` matching (this is true for other databases as well). But there are a few alternatives that are worth exploring. The choice highly depends on your use. One example is indexing word stems instead of the whole word.\n\nA stemming algorithm is a process of linguistic normalization in which the variant forms of a word are reduced to a common form:\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\nPopular algorithms are the [Porter stemming algorithm](https://tartarus.org/martin/PorterStemmer/) and the [Snowball stemming algorithms](https://snowballstem.org/algorithms/).\n\nThere are also more advanced forms like [lemmatization](https://en.wikipedia.org/wiki/Lemmatisation).\n\n## Phonetic algorithms\n\nA [phonetic algorithm](https://en.wikipedia.org/wiki/Phonetic_algorithm) is an algorithm for indexing words by their pronunciation. In other words, it allows you to find words that sound similar to the ones you are looking for.\n\n:::warning\nMost phonetic algorithms only support a single language.\n:::\n\n### Soundex\n\n[Soundex](https://en.wikipedia.org/wiki/Soundex) is a phonetic algorithm for indexing names by sound, as pronounced in English. The goal is for homophones to be encoded to the same representation so they can be matched despite minor differences in spelling. It is a straightforward algorithm, and there are multiple improved versions.\n\nUsing this algorithm, both `\"Robert\"` and `\"Rupert\"` return the string `\"R163\"` while `\"Rubin\"` yields `\"R150\"`. `\"Ashcraft\"` and `\"Ashcroft\"` both yield `\"A261\"`.\n\n### Double Metaphone\n\nThe [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) phonetic encoding algorithm is the second generation of this algorithm. It makes several fundamental design improvements over the original Metaphone algorithm.\n\nڈبل میٹافون سلاو، جرمن، سیلٹک، یونانی، فرانسیسی، اطالوی، ہسپانوی، چینی، اور دیگر ماخذ کی انگریزی میں مختلف بے ضابطگیوں کے لیے اکاؤنٹس ہیں۔\n"
  },
  {
    "path": "docs/docs/ur/recipes/multi_isolate.md",
    "content": "---\ntitle:  کثیر الگ تھلگ استعمال\n---\n\n# کثیر الگ تھلگ استعمال\n\nدھاگوں کے بجائے، تمام ڈارٹ کوڈ الگ تھلگ کے اندر چلتا ہے۔ ہر الگ تھلگ کی اپنی یادداشت کا ڈھیر ہوتا ہے، اس بات کو یقینی بناتا ہے کہ الگ تھلگ ریاست میں سے کوئی بھی کسی دوسرے الگ تھلگ سے قابل رسائی نہیں ہے۔\n\nایزار تک ایک ہی وقت میں متعدد الگ تھلگ مقامات سے رسائی حاصل کی جاسکتی ہے، اور یہاں تک کہ دیکھنے والے بھی الگ تھلگ جگہوں پر کام کرتے ہیں۔ اس ترکیب میں، ہم دیکھیں گے کہ ایک کثیر الگ تھلگ ماحول میں اسار کو کیسے استعمال کیا جائے۔\n\n## ایک سے زیادہ الگ تھلگ کب استعمال کریں۔\n\nاسر لین دین متوازی طور پر انجام پاتے ہیں چاہے وہ ایک ہی الگ تھلگ میں چلیں۔ بعض صورتوں میں، متعدد الگ تھلگ مقامات سے اسار تک رسائی حاصل کرنا اب بھی فائدہ مند ہے۔\n\nTاس کی وجہ یہ ہے کہ اسر ڈارٹ آبجیکٹ سے اور ڈیٹا کو انکوڈنگ اور ڈی کوڈ کرنے میں کافی وقت صرف کرتا ہے۔ آپ اسے انکوڈنگ اور ڈی کوڈنگ جیسن (صرف زیادہ موثر) کے طور پر سوچ سکتے ہیں۔ یہ آپریشن آئسولیٹ کے اندر چلتے ہیں جہاں سے ڈیٹا تک رسائی حاصل کی جاتی ہے اور قدرتی طور پر الگ تھلگ میں دوسرے کوڈ کو بلاک کر دیتے ہیں۔ دوسرے الفاظ میں: اسر آپ کے ڈارٹ آئسولیٹ میں کچھ کام انجام دیتا ہے۔\n\nاگر آپ کو صرف چند سو اشیاء کو ایک ساتھ پڑھنے یا لکھنے کی ضرورت ہے، تو اسے یوآئی الگ تھلگ میں کرنا کوئی مسئلہ نہیں ہے۔ لیکن بڑی لین دین کے لیے یا اگر یوآئی تھریڈ پہلے سے مصروف ہے، تو آپ کو الگ الگ الگ استعمال کرنے پر غور کرنا چاہیے۔\n\n## مثال\n\nThe first thing we need to do is to open Isar in the new isolate. Since the instance of Isar is already open in the main isolate, `Isar.open()` will return the same instance.\n\n:::warning\nMake sure to provide the same schemas as in the main isolate. Otherwise, you will get an error.\n:::\n\n`compute()` starts a new isolate in Flutter and runs the given function in it.\n\n```dart\nvoid main() {\n  // Open Isar in the UI isolate\n  final dir = await getApplicationDocumentsDirectory();\n\n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // listen to changes in the database\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // start a new isolate and create 10000 messages\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // after some time:\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// function that will be executed in the new isolate\nFuture createDummyMessages(int count) async {\n  // we don't need the path here because the instance is already open\n  final dir = await getApplicationDocumentsDirectory();\n\n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // we use a synchronous transactions in isolates\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\nThere are a few interesting things to note in the example above:\n\n- `isar.messages.watchLazy()` is called in the UI isolate and is notified of changes from another isolate.\n- Instances are referenced by name. The default name is `default`, but in this example, we set it to `myInstance`.\n- We used a synchronous transaction to create the mesasges. Blocking our new isolate is no problem, and synchronous transactions are a little faster.\n"
  },
  {
    "path": "docs/docs/ur/recipes/string_ids.md",
    "content": "---\ntitle: اسٹرنگ آئی ڈیز\n---\n\n# اسٹرنگ آئی ڈیز\n\nیہ مجھے ملنے والی اکثر درخواستوں میں سے ایک ہے، اس لیے یہاں اسٹرنگ آئی ڈیز کے استعمال سے متعلق ایک سبق ہے۔\n\nایزار مقامی طور پر اسٹرنگ آئی ڈیز کی حمایت نہیں کرتا ہے، اور اس کی ایک اچھی وجہ ہے: انٹیجر آئی ڈیز بہت زیادہ موثر اور تیز ہیں۔ خاص طور پر لنکس کے لیے، اسٹرنگ آئی ڈی کا اوور ہیڈ بہت اہم ہے۔\n\nمیں سمجھتا ہوں کہ بعض اوقات آپ کو بیرونی ڈیٹا ذخیرہ کرنا پڑتا ہے جو UUIDs یا دیگر غیر عددی آئی ڈیز استعمال کرتا ہے۔ میں اسٹرنگ آئی ڈی کو آپ کے آبجیکٹ میں بطور پراپرٹی اسٹور کرنے اور 64 بٹ انٹ بنانے کے لیے تیز رفتار ہیش نفاذ کا استعمال کرنے کی تجویز کرتا ہوں جسے بطور آئی ڈی استعمال کیا جا سکتا ہے۔\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\nاس نقطہ نظر کے ساتھ، آپ کو دونوں جہانوں میں سے بہترین حاصل ہوتا ہے: لنکس کے لیے موثر عددی ڈیز اور اسٹرنگ آئی ڈیز استعمال کرنے کی اہلیت۔\n\n## فاسٹ ہیش فنکشن\n\nمثالی طور پر، آپ کے ہیش فنکشن میں اعلیٰ معیار ہونا چاہیے (آپ کو تصادم نہیں چاہیے) اور تیز ہونا چاہیے۔ میں مندرجہ ذیل نفاذ کو استعمال کرنے کی سفارش کرتا ہوں:\n\n```dart\n/// FNV-1a 64bit hash algorithm optimized for Dart Strings\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\nاگر آپ ایک مختلف ہیش فنکشن کا انتخاب کرتے ہیں، تو یقینی بنائیں کہ یہ 64 بٹ انٹ واپس کرتا ہے اور کرپٹوگرافک ہیش فنکشن استعمال کرنے سے گریز کریں کیونکہ وہ بہت سست ہیں۔\n\n:::warning\nAvoid using `string.hashCode` because it is not guaranteed to be stable across different platforms and versions of Dart.\n:::"
  },
  {
    "path": "docs/docs/ur/schema.md",
    "content": "---\ntitle: اسکیما\n---\n\n# اسکیما\n\nجب آپ اپنی ایپ کا ڈیٹا ذخیرہ کرنے کے لیے ایزار کا استعمال کرتے ہیں، تو آپ مجموعوں کے ساتھ کام کر رہے ہوتے ہیں۔ ایک مجموعہ متعلقہ ایزار ڈیٹا بیس میں ڈیٹا بیس کی میز کی طرح ہے اور اس میں صرف ایک قسم کی ڈارٹ آبجیکٹ ہو سکتی ہے۔ ہر مجموعہ آبجیکٹ متعلقہ مجموعہ میں ڈیٹا کی ایک قطار کی نمائندگی کرتا ہے۔\n\nمجموعہ کی تعریف کو \"اسکیما\" کہا جاتا ہے۔ ایزار جنریٹر آپ کے لیے بھاری بھرکم سامان اٹھائے گا اور زیادہ تر کوڈ تیار کرے گا جس کی آپ کو کلیکشن استعمال کرنے کی ضرورت ہے۔\n\n## مجموعہ کی اناٹومی۔\n\nآپ `کلیکشن` یا `کلیکشن` کے ساتھ کلاس کی تشریح کر کے ہر اسر مجموعہ کی وضاحت کرتے ہیں۔ ایزار مجموعہ میں ڈیٹا بیس میں متعلقہ جدول میں ہر کالم کے لیے فیلڈز شامل ہوتے ہیں، جس میں بنیادی کلید شامل ہوتی ہے۔\n\nدرج ذیل کوڈ ایک سادہ مجموعہ کی ایک مثال ہے جو ایزار، پہلا نام اور آخری نام کے کالموں کے ساتھ ایک `صارف` ٹیبل کی وضاحت کرتا ہے:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\nکسی فیلڈ کو برقرار رکھنے کے لیے، اسر کو اس تک رسائی حاصل ہونی چاہیے۔ آپ اس بات کو یقینی بنا سکتے ہیں کہ ایسر کو کسی فیلڈ تک رسائی حاصل ہے اسے عوامی بنا کر یا گیٹر اور سیٹٹر کے طریقے فراہم کر کے۔\n:::\n\nمجموعہ کو حسب ضرورت بنانے کے لیے چند اختیاری پیرامیٹرز ہیں:\n\n| Config        | Description                                                                                                      |\n| ------------- | ---------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | کنٹرول کریں کہ آیا پیرنٹ کلاسز اور مکسین کے فیلڈز کو اسر میں محفوظ کیا جائے گا۔ بطور ڈیفالٹ فعال۔                  |\n| `accessor`    | آپ کو ڈیفالٹ کلیکشن ایکسیسر کا نام تبدیل کرنے کی اجازت دیتا ہے (مثال کے طور پر `رابطہ` مجموعہ کے لئے `ایزار.رابطہ`)۔ |\n| `ignore`      | کچھ خصوصیات کو نظر انداز کرنے کی اجازت دیتا ہے۔ یہ سپر کلاسز کے لیے بھی قابل احترام ہیں۔                                  |\n\n### ای زار آئی ڈی\n\nہر کلیکشن کلاس کو کسی شے کی منفرد شناخت کرنے والی قسم `آئی ڈی` کے ساتھ ایک آئی ڈی پراپرٹی کی وضاحت کرنی ہوتی ہے۔ `آئی ڈی` `انٹ` کا صرف ایک عرف ہے جو ای زار جنریٹر کو آئی ڈی کی خاصیت کو پہچاننے کی اجازت دیتا ہے۔\n\nای زار خود بخود آئی ڈی فیلڈز کو انڈیکس کرتا ہے، جو آپ کو ان کی شناخت کی بنیاد پر اشیاء کو مؤثر طریقے سے حاصل کرنے اور ان میں ترمیم کرنے کی اجازت دیتا ہے۔\n\nآپ یا تو خود ids سیٹ کر سکتے ہیں یا ای زار سے ایک آٹو انکریمنٹ آئی ڈی تفویض کرنے کو کہہ سکتے ہیں۔ اگر `آئی ڈی` فیلڈ `نل` ہے اور `حتمی` نہیں ہے تو ای زار ایک خودکار اضافہ آئی ڈی تفویض کرے گا۔ اگر آپ غیر منسوخ آٹو انکریمنٹ آئی ڈی چاہتے ہیں تو آپ `نل` کی بجائے `ای زار.آٹوانکریمنٹ` استعمال کر سکتے ہیں۔\n\n:::tip\nجب کسی چیز کو حذف کیا جاتا ہے تو آٹو انکریمنٹ آئی ڈیز دوبارہ استعمال نہیں کی جاتی ہیں۔ آٹو انکریمنٹ آئی ڈی کو دوبارہ ترتیب دینے کا واحد طریقہ ڈیٹا بیس کو صاف کرنا ہے۔\n:::\n\n### مجموعوں اور فیلڈز کا نام تبدیل کرنا\n\nپہلے سے طے شدہ طور پر، ای زار کلاس کا نام مجموعہ کے نام کے طور پر استعمال کرتا ہے۔ اسی طرح، ای زار ڈیٹا بیس میں فیلڈ کے ناموں کو کالم کے نام کے طور پر استعمال کرتا ہے۔ اگر آپ چاہتے ہیں کہ کسی مجموعہ یا فیلڈ کا نام مختلف ہو، تو `@نام` تشریح شامل کریں۔ درج ذیل مثال جمع کرنے اور فیلڈز کے لیے حسب ضرورت ناموں کو ظاہر کرتی ہے:\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\nخاص طور پر اگر آپ ڈارٹ فیلڈز یا کلاسز کا نام تبدیل کرنا چاہتے ہیں جو پہلے سے ڈیٹا بیس میں محفوظ ہیں، آپ کو `@نام` تشریح استعمال کرنے پر غور کرنا چاہیے۔ بصورت دیگر، ڈیٹا بیس فیلڈ یا مجموعہ کو حذف کر کے دوبارہ تخلیق کر دے گا۔\n\n### Ignoring fields\n\nIsar persists all public fields of a collection class. By annotating a property or getter with `@ignore`, you can exclude it from persistence, as shown in the following code snippet:\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\nIn cases where a collection inherits fields from a parent collection, it's usually easier to use the `ignore` property of the `@Collection` annotation:\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\nاگر کسی مجموعے میں ایک ایسی فیلڈ ہے جس کی قسم ایسر کے ذریعہ تعاون یافتہ نہیں ہے، تو آپ کو فیلڈ کو نظر انداز کرنا ہوگا۔\n\n:::warning\nاس بات کو ذہن میں رکھیں کہ ایسیر اشیاء میں معلومات کو ذخیرہ کرنا اچھا عمل نہیں ہے جو برقرار نہیں ہیں۔\n:::\n\n## تائید شدہ اقسام\n\nای زار درج ذیل ڈیٹا کی اقسام کی حمایت کرتا ہے:\n\n- `bool`\n- `int`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<int>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\nAdditionally, embedded objects and enums are supported. We'll cover those below.\n\n##بائٹ، مختصر، فلوٹ\n\nبہت سے استعمال کے معاملات کے لیے، آپ کو 64 بٹ انٹیجر یا ڈبل ​​کی پوری رینج کی ضرورت نہیں ہے۔ ای ار اضافی اقسام کی حمایت کرتا ہے جو آپ کو چھوٹے نمبروں کو ذخیرہ کرتے وقت جگہ اور میموری کو بچانے کی اجازت دیتا ہے۔\n\n| Type       | Size in bytes | Range                                                   |\n| ---------- |-------------- | ------------------------------------------------------- |\n| **byte**   | 1             | 0 to 255                                                |\n| **short**  | 4             | -2,147,483,647 to 2,147,483,647                         |\n| **int**    | 8             | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |\n| **float**  | 4             | -3.4e38 to 3.4e38                                       |\n| **double** | 8             | -1.7e308 to 1.7e308                                     |\n\nThe additional number types are just aliases for the native Dart types, so using `short`, for example, works the same as using `int`.\n\nHere is an example collection containing all of the above types:\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\nAll number types can also be used in lists. For storing bytes, you should use `List<byte>`.\n\n## کالعدم اقسام\n\nUnderstanding how nullability works in Isar is essential: Number types do **NOT** have a dedicated `null` representation. Instead, a specific value is used:\n\n| Type       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` | \n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN` |\n| **double** |  `double.NaN` |\n\n`bool`, `String`, and `List` have a separate `null` representation.\n\nیہ رویہ کارکردگی کو بہتر بنانے کے قابل بناتا ہے، اور یہ آپ کو نل اقدار کو ہینڈل کرنے کے لیے منتقلی یا خصوصی کوڈ کی ضرورت کے بغیر اپنے فیلڈز کی منسوخی کو آزادانہ طور پر تبدیل کرنے کی اجازت دیتا ہے۔\n\n:::warning\nThe `byte` type does not support null values.\n:::\n\n## تاریخ وقت\n\nIsar does not store timezone information of your dates. Instead, it converts `DateTime`s to UTC before storing them. Isar returns all dates in local time.\n\n`DateTime`s are stored with microsecond precision. In browsers, only millisecond precision is supported because of JavaScript limitations.\n\n## اینوم\n\nایزار دیگر ایزار اقسام کی طرح اینومز کو ذخیرہ کرنے اور استعمال کرنے کی اجازت دیتا ہے۔ تاہم، آپ کو انتخاب کرنا ہوگا کہ اسر ڈسک پر موجود اینوم کی نمائندگی کیسے کرے۔ ایزار چار مختلف حکمت عملیوں کی حمایت کرتا ہے:\n\n| EnumType    | Description \n| ----------- | -----------\n| `ordinal`   | The index of the enum is stored as `byte`. This is very efficient but does not allow nullable enums |\n| `ordinal32` | The index of the enum is stored as `short` (4-byte integer).                                        |\n| `name`      | The enum name is stored as `String`.                                                                |\n| `value`     | A custom property is used to retrieve the enum value.                                               |\n\n:::warning\n`ordinal` and `ordinal32` depend on the order of the enum values. If you change the order, existing databases will return incorrect values.\n:::\n\nآئیے ہر حکمت عملی کے لیے ایک مثال دیکھیں۔\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // same as EnumType.ordinal\n  late TestEnum byteIndex; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // cannot be nullable\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\nیقینا، اینمز کو فہرستوں میں بھی استعمال کیا جا سکتا ہے۔\n\n## ایمبیڈڈ اشیاء\n\nآپ کے کلیکشن ماڈل میں گھریلو اشیاء کا ہونا اکثر مددگار ہوتا ہے۔ اس کی کوئی حد نہیں ہے کہ آپ اشیاء کو کتنی گہرائی میں گھونسلا سکتے ہیں۔ تاہم، ذہن میں رکھیں کہ گہرے اندر کی چیز کو اپ ڈیٹ کرنے کے لیے پورے آبجیکٹ ٹری کو ڈیٹا بیس میں لکھنے کی ضرورت ہوگی۔\n  \n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\nایمبیڈڈ اشیاء کالعدم ہوسکتی ہیں اور دیگر اشیاء کو بڑھا سکتی ہیں۔ صرف ضرورت یہ ہے کہ وہ `@ایمبڈیڈ` کے ساتھ تشریح شدہ ہوں اور بغیر مطلوبہ پیرامیٹرز کے ڈیفالٹ کنسٹرکٹر ہوں۔\n"
  },
  {
    "path": "docs/docs/ur/transactions.md",
    "content": "---\ntitle: لین دین\n---\n\n# لین دین\n\nای زار میں، لین دین کام کی ایک اکائی میں متعدد ڈیٹا بیس آپریشنز کو یکجا کرتا ہے۔ اسر کے ساتھ زیادہ تر تعاملات لین دین کا استعمال کرتے ہیں۔ اسار میں پڑھنے اور لکھنے کی رسائی [ACID](http://en.wikipedia.org/wiki/ACID) کے مطابق ہے۔ اگر کوئی غلطی ہوتی ہے تو ٹرانزیکشنز خود بخود واپس ہو جاتی ہیں۔\n\n## واضح لین دین\n\nایک واضح لین دین میں، آپ کو ڈیٹا بیس کا ایک مستقل سنیپ شاٹ ملتا ہے۔ لین دین کی مدت کو کم سے کم کرنے کی کوشش کریں۔ لین دین میں نیٹ ورک کالز یا دیگر طویل عرصے سے چلنے والے آپریشنز کرنا منع ہے۔\n\nلین دین (خاص طور پر لین دین لکھیں) کی ایک قیمت ہوتی ہے، اور آپ کو ہمیشہ ایک ہی لین دین میں یکے بعد دیگرے آپریشنز کو گروپ کرنے کی کوشش کرنی چاہیے۔\n\nلین دین یا تو مطابقت پذیر یا غیر مطابقت پذیر ہوسکتے ہیں۔ ہم وقت ساز لین دین میں، آپ صرف مطابقت پذیر کارروائیوں کا استعمال کر سکتے ہیں۔ غیر مطابقت پذیر لین دین میں، صرف اسینک آپریشنز۔\n\n|              | Read         | Read & Write       |\n|--------------|--------------|--------------------|\n| Synchronous  | `.txnSync()` | `.writeTxnSync()`  |\n| Asynchronous | `.txn()`     | `.writeTxn()`      |\n\n\n### لین دین پڑھیں\n\nواضح پڑھنے والے لین دین اختیاری ہیں، لیکن وہ آپ کو ایٹم ریڈز کرنے اور لین دین کے اندر موجود ڈیٹا بیس کی مستقل حالت پر انحصار کرنے کی اجازت دیتے ہیں۔ داخلی طور پر ای ذار تمام پڑھنے والے آپریشنز کے لیے ہمیشہ مضمر پڑھنے والے لین دین کا استعمال کرتا ہے۔\n\n:::tip\nاےسنک پڑھنے والے لین دین دوسرے پڑھنے اور لکھنے والے لین دین کے متوازی چلتے ہیں۔ بہت اچھا، ٹھیک ہے؟\n:::\n\n### لین دین لکھیں۔\n\nپڑھنے کی کارروائیوں کے برعکس، اسار میں تحریری کارروائیوں کو ایک واضح لین دین میں لپیٹنا ضروری ہے۔\n\nجب تحریری لین دین کامیابی کے ساتھ ختم ہوجاتا ہے، تو یہ خود بخود کمٹڈ ہوجاتا ہے، اور تمام تبدیلیاں ڈسک پر لکھی جاتی ہیں۔ اگر کوئی خرابی پیش آتی ہے تو، لین دین کو روک دیا جاتا ہے، اور تمام تبدیلیاں واپس کر دی جاتی ہیں۔ ٹرانزیکشنز \"سب یا کچھ نہیں\" ہیں: یا تو ٹرانزیکشن کے اندر تمام تحریریں کامیاب ہوتی ہیں، یا ان میں سے کوئی بھی ڈیٹا کی مستقل مزاجی کی ضمانت کے لیے اثر انداز نہیں ہوتا ہے۔\n\n:::warning\nجب ڈیٹا بیس آپریشن ناکام ہوجاتا ہے، تو لین دین ختم ہوجاتا ہے اور اسے مزید استعمال نہیں کیا جانا چاہیے۔ یہاں تک کہ اگر آپ ڈارٹ میں غلطی کو پکڑتے ہیں۔\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// GOOD\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// BAD: move loop inside transaction\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/ur/tutorials/quickstart.md",
    "content": "---\ntitle: فورا شروع کریں\n---\n\n  # فورا شروع کریں \n\nخوشی کی بات ہے،آپ یہاں ہیں! آئیے وہاں موجود بہترین فلٹر ڈیٹابیس کا استعمال شروع کریں۔\nہم اس فورا شروع کرتے ہیں میں الفاظ میں مختصر اور کوڈ پر تیز ہونے جا رہے ہیں۔\n\n## 1. انحصار شامل کریں۔\nتفریح کا آغاز کرنےسے پہلےہمیں \"پب سپیک۔یمل\" میں چند پیکجز شامل کرنے کی ضرورت ہے۔ہم اپنے لیے بھاری سامان اٹھانے کے لیے پب کا استعمال کر سکتے ہیں۔\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. کلاسوں کی تشریح کریں۔\n\nاپنی کلیکشن کلاسز کو \"کلیکشن@\" کے ساتھ تشریح کریں اورایک \"آئی ڈی@\" فیلڈ کا انتخاب کریں۔\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement;  // you can also use id = null to auto increment\n\n  String? name;\n  int? age;\n}\n```\n\nآئی ڈیز مجموعہ میں اشیاء کی منفرد شناخت کرتی ہیں اور آپ کو بعد میں انہیں دوبارہ تلاش کرنے کی اجازت دیتی ہیں۔\n\n## 3. کوڈ جنریٹر چلائیں۔\n\nشروع کرنے کے لیے درج ذیل کمانڈ پر عمل کریں \"بیلڈ_رنر\"؛\n\n```\ndart run build_runner build\n```\n\nاگر آپ فلٹر استعمال کر رہے ہیں تو درج ذیل استعمال کریں؛\n\n```\nflutter pub run build_runner build\n```\n\n## 4. ای زار مثال کھولیں۔\n\n   ایک نیا ای زار مثال کھولیں اور اپنے تمام کلیکشن اسکیموں کو پاس کریں۔ اختیاری طور پر آپ مثال کا نام اور ڈائریکٹری بتا سکتے ہیں۔\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. لکھیں اور پڑھیں\n\nایک بار آپ کا مثال کھلنے کے بعد، آپ مجموعے کا استعمال شروع کر سکتے ہیں۔\n\n  تمام بنیادی کرڈ آپریشنز \"ای زار کلیکشن\"  کے ذریعے دستیاب ہیں۔\n\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser);\n  داخل کریں اور تروتازہ کریں۔//\n});\n\nfinal existingUser = await isar.users.get(newUser.id);\n حاصل کریں۔//\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!);\n  حذف کریں//\n});\n```\n\n## دیگر وسائل\n\nکیا آپ بصری سیکھنے والے ہیں؟ ای زار کے ساتھ شروع کرنے کے لیے یہ ویڈیوز دیکھیں:\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/ur/watchers.md",
    "content": "---\ntitle: نگران\n---\n\n# نگران\n\nای زار آپ کو ڈیٹا بیس میں ہونے والی تبدیلیوں کو سبسکرائب کرنے کی اجازت دیتا ہے۔ آپ کسی مخصوص شے، پورے مجموعہ، یا کسی سوال میں تبدیلیوں کے لیے \"دیکھ\" سکتے ہیں۔\n\nنگہبان آپ کو ڈیٹا بیس میں ہونے والی تبدیلیوں پر موثر انداز میں رد عمل ظاہر کرنے کے قابل بناتے ہیں۔ مثال کے طور پر جب کوئی رابطہ شامل کیا جاتا ہے تو آپ اپنا یوآئی دوبارہ بنا سکتے ہیں، جب کوئی دستاویز اپ ڈیٹ ہو جائے تو نیٹ ورک کی درخواست بھیج سکتے ہیں، وغیرہ۔\n\nلین دین کے کامیابی سے انجام پانے اور ہدف میں تبدیلی کے بعد دیکھنے والے کو مطلع کیا جاتا ہے۔\n\n##آبجیکٹ دیکھنا\n\nاگر آپ چاہتے ہیں کہ کسی مخصوص چیز کے بننے، اپ ڈیٹ یا حذف ہونے پر آپ کو مطلع کیا جائے، تو آپ کو کسی چیز کو دیکھنا چاہیے:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\nجیسا کہ آپ اوپر کی مثال میں دیکھ سکتے ہیں، آبجیکٹ کو ابھی موجود ہونے کی ضرورت نہیں ہے۔ دیکھنے والے کو اس کے بننے پر مطلع کیا جائے گا۔\n\nThere is an additional parameter `fireImmediately`. If you set it to `true`, Isar will immediately add the object's current value to the stream.\n\n### سست دیکھ رہا ہے۔\n\nہوسکتا ہے کہ آپ کو نئی قیمت وصول کرنے کی ضرورت نہ ہو لیکن صرف تبدیلی کے بارے میں مطلع کیا جائے۔ یہ ای زار کو آبجیکٹ لانے سے بچاتا ہے:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## کلیکشن دیکھ رہے ہیں۔\n\nکسی ایک شے کو دیکھنے کے بجائے، آپ ایک پورا مجموعہ دیکھ سکتے ہیں اور کسی بھی چیز کو شامل، اپ ڈیٹ یا حذف کیے جانے پر مطلع کر سکتے ہیں:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## سوالات دیکھ رہے ہیں۔\n\nپورے سوالات کو دیکھنا بھی ممکن ہے۔ ای زار صرف آپ کو مطلع کرنے کی پوری کوشش کرتا ہے جب سوال کے نتائج حقیقت میں تبدیل ہوں۔ آپ کو مطلع نہیں کیا جائے گا اگر لنکس استفسار کو تبدیل کرنے کا سبب بنتے ہیں۔ اگر آپ کو لنک کی تبدیلیوں کے بارے میں مطلع کرنے کی ضرورت ہو تو کلیکشن واچر کا استعمال کریں۔\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nاگر آپ آفسیٹ اور لمیٹ یا الگ الگ سوالات استعمال کرتے ہیں، تو آئسر آپ کو اس وقت بھی مطلع کرے گا جب اشیاء فلٹر سے مماثل ہوں لیکن استفسار سے باہر، نتائج تبدیل ہوتے ہیں۔\n:::\n\nJust like `watchObject()`, you can use `watchLazy()` to get notified when the query results change but not fetch the results.\n\n:::danger\nہر تبدیلی کے لیے استفسارات کو دوبارہ چلانا بہت ناکارہ ہے۔ اس کے بجائے اگر آپ سست کلیکشن واچر کا استعمال کریں تو بہتر ہوگا۔\n:::\n"
  },
  {
    "path": "docs/docs/watchers.md",
    "content": "---\ntitle: Watchers\n---\n\n# Watchers\n\nIsar allows you to subscribe to changes in the database. You can \"watch\" for changes in a specific object, an entire collection, or a query.\n\nWatchers enable you to react to changes in the database efficiently. You can for example rebuild your UI when a contact is added, send a network request when a document is updated, etc.\n\nA watcher is notified after a transaction commits successfully and the target actually changes.\n\n## Watching Objects\n\nIf you want to be notified when a specific object is created, updated or deleted, you should watch an object:\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// prints: User changed: Mark\n\nawait isar.users.delete(5);\n// prints: User changed: null\n```\n\nAs you can see in the example above, the object does not need to exist yet. The watcher will be notified when it is created.\n\nThere is an additional parameter `fireImmediately`. If you set it to `true`, Isar will immediately add the object's current value to the stream.\n\n### Lazy watching\n\nMaybe you don't need to receive the new value but only be notified about the change. That saves Isar from having to fetch the object:\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// prints: User 5 changed\n```\n\n## Watching Collections\n\nInstead of watching a single object, you can watch an entire collection and get notified when any object is added, updated, or deleted:\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// prints: A User changed\n```\n\n## Watching Queries\n\nIt is even possible to watch entire queries. Isar does its best to only notify you when the query results actually change. You will not be notified if links cause the query to change. Use a collection watcher if you need to be notified about link changes.\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// prints: Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// prints: Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// no print\n\nawait isar.users.put(User()..name = 'Antonia');\n// prints: Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\nIf you use offset & limit or distinct queries, Isar will also notify you when objects match the filter but outside the query, results change.\n:::\n\nJust like `watchObject()`, you can use `watchLazy()` to get notified when the query results change but not fetch the results.\n\n:::danger\nRerunning queries for every change is very inefficient. It would be best if you used a lazy collection watcher instead.\n:::\n"
  },
  {
    "path": "docs/docs/zh/README.md",
    "content": "---\nhome: true\ntitle: 主页\nheroImage: /isar.svg\nactions:\n  - text: 让我们开始吧\n    link: /zh/tutorials/quickstart.html\n    type: primary\nfeatures:\n  - title: 💙 专门为 Flutter 打造\n    details: 简化设置，易于使用，几行代码即可开始使用，无样板代码。\n  - title: 🚀 高可扩展性\n    details: 单个 NoSQL 数据库实例即能支持存入数十万的数据并能保证高速的异步查询。\n  - title: 🍭 多功能性\n    details: Isar 集成了很多现有功能来帮助你管理数据：包括但不限于复合索引和多条目索引、查询修改器、支持 JSON 等。\n  - title: 🔎 全文检索\n    details: Isar 内部支持全文检索。创建一个多条目索引然后进行查询将变得十分简单。\n  - title: 🧪 ACID 语义\n    details: Isar 兼容 ACID 语义并能自动处理事务。倘若遇到错误会自动回滚。\n  - title: 💃 类型静态\n    details: Isar 的查询都是静态的，即在编译时就已确定变量。完全无需担心运行时错误。\n  - title: 📱 支持多平台\n    details: iOS、 Android、桌面端以及 Web 端！\n  - title: ⏱ 异步多线程\n    details: 并行查询 & 多线程支持开箱即用\n  - title: 🦄 开源\n    details: 所有一切都是开源并永久免费！\n\nfooter: Apache Licensed | Copyright © 2022 Simon Leier\n---\n"
  },
  {
    "path": "docs/docs/zh/crud.md",
    "content": "---\ntitle: 增删改查\n---\n\n# 增删改查（CRUD）\n\n当你已经定义了 Collection，现在来学习如何对其操作。\n\n## 创建一个 Isar 实例\n\n首先我们必须创建一个 Isar 实例。每一个实例需要一个可写的路径来保存数据库文件。倘若你未指定路径，Isar 会根据当前设备所属平台来自动选择合适的默认路径。\n\n将你想要使用的所有 Collection 的 Schema 作为参数传入到创建实例的方法中。如果你有多个实例，你仍然需要给每个实例配置相同的 Schema（即各个实例的 Schema 必须一致）。\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [RecipeSchema],\n  directory: dir.path,\n);\n```\n\n你可以使用默认配置，也可以根据下表修改参数：\n\n| 参数配置            | 描述                                                                                                                              |\n| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| `name`              | 以不同名称创建多个实例。默认情况下，`\"default\"` 会被用作实例名称。                                                                |\n| `directory`         | 该实例数据库文件的存储路径。默认情况下，iOS 是 `NSDocumentDirectory`，而 Android 则用 `getDataDirectory` 返回的结果，Web 端可选。 |\n| `relaxedDurability` | 放宽可靠性来提高写入性能。倘若应用遇到系统崩溃（不是 App 的崩溃），允许丢弃最后一次提交的事务操作结果。数据库文件损毁是不可能的。 |\n| `compactOnLaunch`   | 是否以数据库压缩的形式来启用实例。                                                                                                |\n| `inspector`         | 在开发调试阶段启用检查器 Inspector。 对于 profile 和 release 版本，该参数会被忽略。                                               |\n\n倘若一个实例已经被创建，调用 `Isar.open()` 会无视传入的参数，直接返回该实例。这使得在单一 isolate 内使用 Isar 会很有用。\n\n:::tip\n考虑使用 [path_provider](https://pub.dev/packages/path_provider) 来获取所有平台的有效路径。\n:::\n\n数据库文件的路径在 `directory/name.isar`。\n\n## 从数据库中读取数据\n\n对于给定类型，通过调用 `IsarCollection` 来查找、查询以及创建新的对象。\n\n在下方的例子中，我们假定有一个 `Recipe` Collection，其定义如下：\n\n```dart\n@collection\nclass Recipe {\n  Id? id;\n\n  String? name;\n\n  DateTime? lastCooked;\n\n  bool? isFavorite;\n}\n```\n\n### 获取 Collection\n\n你声明的所有 Collection 都存在于 Isar 实例中（只要它们的 Schema 在创建实例的时候被传入了）。你可以通过下面代码来读取菜单数据：\n\n```dart\nfinal recipes = isar.recipes;\n```\n\n就这么简单！如果你不想用 Collection 的访问名（这里即 recipes），也可以调用 `collection()` 方法：\n\n```dart\nfinal recipes = isar.collection<Recipe>();\n```\n\n### 通过 Id 来获取数据对象\n\n我们的 Collection 中还没有数据。但是假设已有数据，我们可以通过以下代码来访问 Id 为 `123` 的菜单。\n\n```dart\nfinal recipe = await isar.recipes.get(123);\n```\n\n`get()` 返回一个包含对象的 `Future`，如果对象不存在，则返回 `null`。 默认情况下 Isar 所有的操作均为异步，而大部分操作也有其对应的同步处理方法，如：\n\n```dart\nfinal recipe = isar.recipes.getSync(123);\n```\n\n:::tip\n因为 Isar 已经足够快了，所以你应该在 UI isolate 中尽可能使用默认的异步方法。当然使用对应的同步方法也是可接受的。\n:::\n\n如果你想要同时获取多个对象数据， 使用 `getAll()` 或 `getAllSync()`：\n\n```dart\nfinal recipe = await isar.recipes.getAll([1, 2]);\n```\n\n### 查询对象\n\n除了通过 Id 来获取对象数据，你也可以通过 `.where()` 和 `.filter()` 来查询匹配指定条件的多个对象，其返回的是数组 List:\n\n```dart\nfinal allRecipes = await isar.recipes.where().findAll();\n\nfinal favouires = await isar.recipes.filter()\n  .isFavoriteEqualTo(true)\n  .findAll();\n```\n\n➡️ 学习更多：[查询](queries)\n\n## 修改数据库\n\n终于到了修改数据的时候了！ 在一个写入事务（Write Transaction）中使用对应的操作序列来创建、修改和删除对象：\n\n```dart\nawait isar.writeTxn(() async {\n  final recipe = await isar.recipes.get(123)\n\n  recipe.isFavorite = false;\n  await isar.recipes.put(recipe); // 修改数据\n\n  await isar.recipes.delete(123); // 或者删除数据\n});\n```\n\n➡️ 学习更多：[事务](transactions)\n\n### 插入对象\n\n通过插入对象到 Collection，即可保存其数据到 Isar 数据库中。 Isar 的`put()` 方法会创建或者覆盖对象数据，取决于该对象是否已经存在于数据库里。\n\n如果一个字段是 `null` 或 `Isar.autoIncrement`，Isar 则会分配一个自增 Id 来表示。\n\n```dart\nfinal pancakes = Recipe()\n  ..name = 'Pancakes'\n  ..lastCooked = DateTime.now()\n  ..isFavorite = true;\n\nawait isar.writeTxn(() async {\n  await isar.recipes.put(pancakes);\n})\n```\n\n如果 `id` 不为 final， 那么 Isar 会自动将这个 Id 分配给该对象。\n\n同时插入多个对象也很简单：\n\n```dart\nawait isar.writeTxn(() async {\n  await isar.recipes.putAll([pancakes, pizza]);\n})\n```\n\n### 修改对象\n\n`collection.put(object)` 方法兼有创建和修改的功能。如果一个对象的 Id 是 `null` （或者不存在），它就会被创建；否则，它就会被修改。\n\n所以如果我们想要取消喜欢煎饼的话，可以做以下操作：\n\n```dart\nawait isar.writeTxn(() async {\n  pancakes.isFavorite = false;\n  await isar.recipes.put(recipe);\n});\n```\n\n### 删除对象\n\n想要从 Isar 数据库中删除一个对象？用 `collection.delete(id)` 方法。这个方法会返回指定对象是否被删除（即返回布尔值）。如果你想通过 Id 来删除指定菜单，比如其 Id 为 `123`，你可以用下方代码：\n\n```dart\nawait isar.writeTxn(() async {\n  final success = await isar.recipes.delete(123);\n  print('Recipe deleted: $success');\n});\n```\n\n相似地，也有对应的批量删除方法，其返回结果是被删除对象的数量：\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.deleteAll([1, 2, 3]);\n  print('We deleted $count recipes');\n});\n```\n\n如果你不知道你想删除对象的 Id，你可以先通过指定条件来查询：\n\n```dart\nawait isar.writeTxn(() async {\n  final count = await isar.recipes.filter()\n    .isFavoriteEqualTo(false)\n    .deleteAll();\n  print('We deleted $count recipes');\n});\n```\n"
  },
  {
    "path": "docs/docs/zh/faq.md",
    "content": "---\ntitle: 常见疑问\n---\n\n# 常见疑问\n\n关于 Isar 和 Flutter 数据库的常见问题集合。\n\n### 为什么我需要一个数据库？\n\n> 我把数据存在后台服务器的数据库，为什么还需要 Isar？\n\n即便在今天，你可能也会遇到没有网络连接的时候，比如在地铁里、飞机上或者拜访老一辈的时候（你懂的）。网络不好也就意味着无法连接到后台数据库，所以你应该用离线数据库来增强你 App 的使用体验！\n\n### Isar 对比 Hive\n\n答案很简单：Isar [就是为了代替 Hive 而生的](https://github.com/hivedb/hive/issues/246)，现阶段我都会毫不犹豫地推荐 Isar 而不是 Hive。\n\n### Where 子句？！\n\n> 为什么 **_我_** 必须选择使用何种索引？\n\n有多方面的原因。许多数据库会针对给定查询启发式选择最佳索引。数据库需要收集额外的使用信息（-> 额外的性能开销），而且可能仍然会选到错误的索引，从而导致查询变得更慢。\n\n没有人比你，作为开发者，更了解你的数据。所以你大可根据实际情况选择最优索引，甚至可以决定是否需要用索引来做查询或排序。\n\n### 我是否必须用索引或 where 子句?\n\n不必！如果你只用 Filter，Isar 也已经足够快了。\n\n### Isar 真的足够快？\n\n在针对移动端的数据库中，Isar 在性能方面名列前茅，所以对于大多数应用场景，它本身已经足够快了。如果你遇到了性能问题，很大可能是哪里做得不对。\n\n### Isar 会增加我 App 的打包大小吗？\n\n是的，但是很小。 Isar 会给你的 App 增加 1 - 1.5 MB 的下载大小，Web 端则仅增加几 KB。\n\n### 文档有疏漏或错误。\n\n好吧，对不起。 请[在此开一个 Issue](https://github.com/isar-community/isar/issues/new/choose) 或者更好的话，提交一个 PR 来帮助修复 💪。\n"
  },
  {
    "path": "docs/docs/zh/indexes.md",
    "content": "---\ntitle: 索引\n---\n\n# 索引（Index）\n\n索引是 Isar 最重要的功能。所有嵌入式数据库都提供了“普通”索引功能（如果有的话），但是 Isar 支持组合搜索引和多条目索引。理解索引的工作原理是优化查询性能的基本前提。你可以选择使用哪种索引以及如何使用它们。我们先从索引的简介开始。\n\n## 什么是索引？\n\n当一个 Collection 未被索引时，数据行的顺序很大可能无法被查询所识别，也就无从优化查询性能。因此查询不得不线性地搜索所有对象。也就是说，必须对每个对象进行查询，看它是否符合查询条件。你可以想象，这会耗费不少时间。对每个对象进行查询不是很高效。\n\n举例来说，这个 `Product` Collection 是完全无序的。\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  late int price;\n}\n```\n\n**数据：**\n\n| id  | name      | price |\n| --- | --------- | ----- |\n| 1   | Book      | 15    |\n| 2   | Table     | 55    |\n| 3   | Chair     | 25    |\n| 4   | Pencil    | 3     |\n| 5   | Lightbulb | 12    |\n| 6   | Carpet    | 60    |\n| 7   | Pillow    | 30    |\n| 8   | Computer  | 650   |\n| 9   | Soap      | 2     |\n\n如果要找出价格超过 30 欧元的商品时，就需要查询 9 行数据。虽然 9 行数据不多问题不大，但是如果需要查询十万行那就是很大的问题了。\n\n```dart\nfinal expensiveProducts = await isar.products.filter()\n  .priceGreaterThan(30)\n  .findAll();\n```\n\n为了改善查询性能，我们对 `price` 属性进行了索引。一个索引就像是一张有序的查询表：\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String name;\n\n  @Index()\n  late int price;\n}\n```\n\n**生成的索引表:**\n\n| price                | id                 |\n| -------------------- | ------------------ |\n| 2                    | 9                  |\n| 3                    | 4                  |\n| 12                   | 5                  |\n| 15                   | 1                  |\n| 25                   | 3                  |\n| 30                   | 7                  |\n| <mark>**55**</mark>  | <mark>**2**</mark> |\n| <mark>**60**</mark>  | <mark>**6**</mark> |\n| <mark>**650**</mark> | <mark>**8**</mark> |\n\n现在查询就会快多了。Isar 会直接从底下三行通过 Id 找出它们对应的对象。\n\n### 排序\n\n另一个比较酷的是：索引支持超快的排序。对查询结果排序往往很耗性能，因为数据库必须加载所有的数据，将它们暂时放在内存，然后对它们排序。即使你指定了偏移量或限制，但它俩是在排序完成后才会被执行的。\n\n假定我们想要找出四个最便宜的商品。我们可以使用下方查询代码：\n\n```dart\nfinal cheapest = await isar.products.filter()\n  .sortByPrice()\n  .limit(4)\n  .findAll();\n```\n\n在这个例子中，数据库必须加载所有（！）商品数据，按照价格给它们排序，然后返回四个价格最低的商品。\n\n你或许会想到，用之前的索引来做应该会更高效。数据库直接读取索引表的前四行，然后返回它们所对应的商品数据，因为索引表默认是已经按照索引属性的大小顺序排好了的。\n\n我们通过下方代码来实现：\n\n```dart\nfinal cheapestFast = await isar.products.where()\n  .anyPrice()\n  .limit(4)\n  .findAll();\n```\n\n这个 `.anyX()` Where 子句告诉 Isar 索引只是用来排序。你同样也可以使用如 `.priceGreaterThan()` 这样的 Where 子句来获取相同结果。\n\n## 唯一索引\n\n唯一索引能保证索引不含重复的值。它可以由一个或多个属性构成。如果一个唯一索引仅包含一个属性，那么其对应的属性值就是唯一的。如果唯一索引由多个属性构成，那这些属性值的组合是唯一的。\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true)\n  late String username;\n\n  late int age;\n}\n```\n\n所有对唯一索引会造成数据重复的写入操作都会造成错误：\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1); // -> 没问题\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\n// 试着写入一个和上面相同用户名的用户数据\nawait isar.users.put(user2); // -> 错误：违反了唯一性约束\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n```\n\n## 替换索引\n\n有时候你可能不愿抛出唯一性约束错误，而是想要用新数据覆盖掉原有数据。那么你可以将对应属性的索引设置为 `replace: true` 来实现。\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  @Index(unique: true, replace: true)\n  late String username;\n}\n```\n\n现在如果我们试着插入一个同用户名的用户数据，Isar 会直接使用新数据覆盖原有数据（这里的原有数据 user1 被新数据 user2 覆盖了，因为属性 username 必须是唯一的）。\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\nawait isar.users.put(user1);\nprint(await isar.user.where().findAll());\n// > [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nprint(await isar.user.where().findAll());\n// > [{id: 2, username: 'user1' age: 30}]\n```\n\n替换索引也提供了 `putBy()` 方法，允许你只更新对象数据，而不是直接覆盖它们。那么现有的 Id 将会被复用，所有关联也会被保留。\n\n```dart\nfinal user1 = User()\n  ..id = 1\n  ..username = 'user1'\n  ..age = 25;\n\n// user1 是第一次被写入数据库，因此这里效果等同于 put() 方法\nawait isar.users.putByUsername(user1);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}]\n\nfinal user2 = User()\n  ..id = 2;\n  ..username = 'user1'\n  ..age = 30;\n\nawait isar.users.put(user2);\nawait isar.user.where().findAll(); // -> [{id: 1, username: 'user1' age: 30}]\n```\n\n你可以看到，此处 Id 被复用了，对象始终是同一个，只是更改了 age 属性。\n\n## 大小写不敏感的索引\n\n所有针对 `String` 和 `List<String>` 属性的索引默认情况下对大小写敏感。如果你想创建一个对大小写不敏感的索引，你可以设置 `caseSensitive` 选项为 `false`：\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  @Index(caseSensitive: false)\n  late String name;\n\n  @Index(caseSensitive: false)\n  late List<String> tags;\n}\n```\n\n## 索引类型\n\n索引有三种不同类型。大多数情况下，你会使用 `IndexType.value` 的值索引，但是哈希索引会更高效。\n\n### 值索引\n\n值索引是默认的索引类型，是唯一可被用于非字符串或非数组类型属性的索引。属性的值将会被用于创建索引。对于数组 List，其包含的元素会被用来创建索引。值索引是三种类型中最灵活但同时也是最占存储空间的索引。\n\n:::tip\n对于原生数据类型（如 Int）的属性，或字符串类型的属性，但需要用到 `startsWith()` Where 子句，亦或是数组类型的属性，需要对其单一元素查询，那么你可以使用 `IndexType.value`。\n:::\n\n### 哈希索引\n\n字符串和数组可以通过散列化索引来大幅度减小存储空间。哈希索引的缺点是它无法通过前缀匹配来搜寻（如使用 `startsWith` 的 Where 子句）。\n\n:::tip\n对于类型为数组或字符串的属性，如果你不会用到 `startsWith` 和 `elementEqualTo` 的 Where 子句，可以使用 `IndexType.hash`。\n:::\n\n### 哈希元素索引\n\n我们可以使用 `IndexType.hash` 来对整个数组或字符串散列化处理，也可以使用 `IndexType.hashElements` 分别对数组中单个元素做散列化，来高效地创建多条目的索引。\n\n:::tip\n对于 `List<String>` 类型的属性，如果你需要用到 `elementEqualTo`的 Where 子句，可以使用 `IndexType.hashElements`。\n:::\n\n## 组合索引\n\n组合索引是指包含多个属性的索引。Isar 允许你创建最多三个属性的组合索引。\n\n组合索引也就是所谓的多列索引。\n\n让我们从示例学习组合索引。我们先创建了一个 Person Collection，然后基于 age 和 name 属性定义了一个组合索引：\n\n```dart\n@collection\nclass Person {\n  Id? id;\n\n  late String name;\n\n  @Index(composite: [CompositeIndex('name')])\n  late int age;\n\n  late String hometown;\n}\n```\n\n**数据：**\n\n| id  | name   | age | hometown  |\n| --- | ------ | --- | --------- |\n| 1   | Daniel | 20  | Berlin    |\n| 2   | Anne   | 20  | Paris     |\n| 3   | Carl   | 24  | San Diego |\n| 4   | Simon  | 24  | Munich    |\n| 5   | David  | 20  | New York  |\n| 6   | Carl   | 24  | London    |\n| 7   | Audrey | 30  | Prague    |\n| 8   | Anne   | 24  | Paris     |\n\n**生成的索引表:**\n\n| age | name   | id  |\n| --- | ------ | --- |\n| 20  | Anne   | 2   |\n| 20  | Daniel | 1   |\n| 20  | David  | 5   |\n| 24  | Anne   | 8   |\n| 24  | Carl   | 3   |\n| 24  | Carl   | 6   |\n| 24  | Simon  | 4   |\n| 30  | Audrey | 7   |\n\n生成的组合索引表包含了所有人的信息，并默认按照他们的年龄和姓名排序。\n\n如果你想要高效地使用多个属性来进行排序并查询，组合索引能帮你轻松实现。它也提供了可同时查询多个属性的进阶版 Where 子句：\n\n```dart\nfinal result = await isar.where()\n  .ageNameEqualTo(24, 'Carl')\n  .hometownProperty()\n  .findAll() // -> ['San Diego', 'London']\n```\n\n组合索引中的最后一个属性也支持查询条件语句如 `startsWith()` 或 `lessThan()`：\n\n```dart\nfinal result = await isar.where()\n  .ageEqualToNameStartsWith(20, 'Da')\n  .findAll() // -> [Daniel, David]\n```\n\n## 多条目索引（全文检索）\n\n如果你用 `IndexType.value` 对一个数组进行索引，Isar 会自动创建多条目索引，数组中每一个元素都会被索引。这适用于所有类型的数组。\n\n多条目索引的实际应用包括对标签数组的索引或者创建全文检索的索引。\n\n```dart\n@collection\nclass Product {\n  Id? id;\n\n  late String description;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get descriptionWords => Isar.splitWords(description);\n}\n```\n\n`Isar.splitWords()` 能将字符串按照 [Unicode Annex #29](https://unicode.org/reports/tr29/) 分解成一个个单词，所以它几乎适用于所有人类语言。\n\n**数据：**\n\n| id  | 字符串                       | 分解结果                     |\n| --- | ---------------------------- | ---------------------------- |\n| 1   | comfortable blue t-shirt     | [comfortable, blue, t-shirt] |\n| 2   | comfortable, red pullover!!! | [comfortable, red, pullover] |\n| 3   | plain red t-shirt            | [plain, red, t-shirt]        |\n| 4   | red necktie (super red)      | [red, necktie, super, red]   |\n\n相同的字符只会在索引中出现一次。\n\n**生成的索引表:**\n\n| 单词        | id        |\n| ----------- | --------- |\n| comfortable | [1, 2]    |\n| blue        | 1         |\n| necktie     | 4         |\n| plain       | 3         |\n| pullover    | 2         |\n| red         | [2, 3, 4] |\n| super       | 4         |\n| t-shirt     | [1, 3]    |\n\n现在这个索引可以使用每个单词的前缀匹配（或等同比较）的 Where 子句了。\n\n:::tip\n你应该也要考虑使用[语音算法](https://en.wikipedia.org/wiki/Phonetic_algorithm)如 [Soundex](https://en.wikipedia.org/wiki/Soundex) 返回的结果，而不是直接存储单词。\n:::\n"
  },
  {
    "path": "docs/docs/zh/limitations.md",
    "content": "# 局限性\n\n你知道 Isar 是跨平台支持移动端、桌面端和 Web 端的，在移动和桌面端它是通过虚拟机来运行的，而 Web 端则不是。两者差异较大且有不同的局限性。\n\n## 虚拟机的局限\n\n- 一个字符串只能用其前 1024 字节来进行 Where 子句的前缀匹配查询\n- 对象的大小只能为 16MB\n\n## Web 端的局限\n\n因为 Isar 在 Web 端依赖于 IndexedDB，所以遇到的限制会更多，但即便如此，在使用 Isar 的过程中基本可以忽略不计。\n\n- 不支持同步操作方法\n- 目前，`Isar.splitWords()` 和 `.matches()` 还不支持 Web 端\n- 不会像在虚拟机中那样严格检查 Schema 的改变，所以必须谨慎对待\n- 所有数字类型将按照双浮点数（JS 中唯一的数字类型）做排序，所以 `@Size32` 不会起作用\n- 索引的原理不同，所以哈希索引无法节省更多存储空间（但用法依然一致）\n- `col.delete()` 和 `col.deleteAll()` 能运行但是返回值不正确\n- `col.clear()` 无法重置自增值\n- `NaN` 尚不支持\n"
  },
  {
    "path": "docs/docs/zh/links.md",
    "content": "---\ntitle: 关联\n---\n\n# 关联（Link）\n\n关联允许你表达对象之间的关系，比如评论的作者（即用户）。你可以使用 Isar 的关联来实现 `1:1`、`1:n` 和 `n:n` 的关系。使用关联比使用嵌套对象更不符人类工程学，因此你应该尽可能使用嵌套对象来代替关联。\n\n你可以将关联理解为包含关系的一张数据表。它和 SQL 的关系很接近，但有一些不同的功能设定和 API。\n\n## IsarLink\n\n`IsarLink<T>` 可以包含最多一个被关联对象，它经常被用于表达对一的关系。 `IsarLink` 有一个叫做 `value` 的属性，它负责存放被关联对象。\n\n关联是懒加载的，因此你需要显式告诉 `IsarLink` 加载并保存 `value` 的值。你可以分别调用 `linkProperty.load()` 和 `linkProperty.save()` 方法。\n\n:::tip\n关联和被关联 Collection 的 Id 不应该为 final。\n:::\n\n对于 Web 端，当你第一次使用一个 Collection 时，它所含的关联会被自动加载。让我们先从添加一个 IsarLink 开始学习：\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teacher = IsarLink<Teacher>();\n}\n```\n\n我们在教师和学生之间定义了一个关联。在例子中，每一个学生对应每一个教师。\n\n首先，我们创建一位数学教师，然后将 TA 分配给一个叫 Linda 的学生。我们需要使用 `.put()` 方法并手动保存关联。\n\n```dart\nfinal mathTeacher = Teacher()..subject = 'Math';\n\nfinal linda = Student()\n  ..name = 'Linda'\n  ..teacher.value = mathTeacher;\n\nawait isar.writeTxn(() async {\n  await isar.students.put(linda);\n  await isar.teachers.put(mathTeacher);\n  await linda.teacher.save();\n});\n```\n\n现在我们可以使用关联：\n\n```dart\nfinal linda = await isar.students.where().nameEqualTo('Linda').findFirst();\n\nfinal teacher = linda.teacher.value; // > Teacher(subject: 'Math')\n```\n\n让我们用同步方法复现一次。我们不需要手动保存关联，因为 `.putSync()` 方法自动会存储所有关联，它甚至帮我们写入了被关联教师的数据。\n\n```dart\nfinal englishTeacher = Teacher()..subject = 'English';\n\nfinal david = Student()\n  ..name = 'David'\n  ..teacher.value = englishTeacher;\n\nisar.writeTxnSync(() {\n  isar.students.putSync(david);\n});\n```\n\n## IsarLinks\n\n在上述例子中，一个学生对应多个教师才更符合实际情况。幸运的是，Isar 也有 `IsarLinks<T>` 来实现对多的关系。\n\n`IsarLinks<T>` 自 `Set<T>` 扩展而来，因此也可使用其相关的方法。\n\n`IsarLinks` 和 `IsarLink` 一样也是懒加载。你可以通过调用 `linkProperty.load()` 来加载所有相关联对象，调用`linkProperty.save()` 来保存。\n\n`IsarLink` 和 `IsarLinks` 的内部实现逻辑是一样的。我们可以将上述例子中的 `IsarLink<Teacher>` 改为 `IsarLinks<Teacher>`，将多个教师数据分配给单个学生（数据不会丢失）。\n\n```dart\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\n可以这么做的原因是我们没有修改关联的名称（`teacher`），所以 Isar 直接使用了之前的数据。\n\n```dart\nfinal biologyTeacher = Teacher()..subject = 'Biology';\n\nfinal linda = isar.students.where()\n  .filter()\n  .nameEqualTo('Linda')\n  .findFirst();\n\nprint(linda.teachers); // {Teacher('Math')}\n\nlinda.teachers.add(biologyTeacher);\n\nawait isar.writeTxn(() async {\n  await linda.teachers.save();\n});\n\nprint(linda.teachers); // {Teacher('Math'), Teacher('Biology')}\n```\n\n## 反向关联\n\n我知道你可能会问要表达反向关系该怎么做。无需担心，现在我们来介绍反向关联。\n\n反向关联字面意义上很好理解，就是相反方向的关联。每个关联总是对应一个隐式的反向关联。为了使用它们，你可以给 `IsarLink` 或 `IsarLinks` 添加 `@Backlink()` 的注解。\n\n反向关联不需要额外的内存或计算资源；所以你可以自由地添加、删除或者给它们改名，而无需担心数据丢失。\n\n如果我们想要知道某一位教师所教的是哪些学生，就可以这么定义反向关联：\n\n```dart\n@collection\nclass Teacher {\n  Id id;\n\n  late String subject;\n\n  @Backlink(to: 'teacher')\n  final student = IsarLinks<Student>();\n}\n```\n\n我们需要给反向关联指定指向的关联。两个对象之间可以拥有多个不同关联。\n\n## 初始化关联\n\n`IsarLink` 和 `IsarLinks` 都有一个无参构造器，用于在对象被创建时分配关联属性。将关联属性声明为 `final` 是正确的做法。\n\n当你第一次使用 `put()` 方法来创建对象时，关联就会被初始化，然后你可以调用 `load()` 和 `save()` 方法。关联在被创建之后就会立即开始记录它所关联属性的数据变化，所以你甚至可以在创建它之前就可以添加或删除对象之间的关系。\n\n:::danger\n将关联移到另一个对象是不符合规范的。\n:::\n"
  },
  {
    "path": "docs/docs/zh/queries.md",
    "content": "---\ntitle: 查询\n---\n\n# 查询\n\n查询是指你如何查找匹配指定条件的数据。例如：\n\n- 查找所有被收藏的联系人\n- 查找联系人列表中名（不是姓）不同的人\n- 删除那些没有写明姓氏的联系人\n\n因为查询是在数据库中而不是在 Dart 中执行的，所以它们非常快。当你巧妙地运用索引，性能将会更大幅度地被提高。下面你将学习如何来查询数据，以及如何提升查询性能。\n\n有两种方法来过滤数据：过滤器 Filter 和 Where 子句。我们先来看 Filter 的用法。\n\n## Filter\n\nFilter 很好理解也很容易使用。Isar Generator 会根据 Collection 中字段的类型来生成多种 Filter，其中大部分 Filter 的名称也解释了它们的用途。\n\nFilter 通过特定条件表达式来匹配 Collection 中每一个待查询对象。如果该表达式返回 `true`，那么 Isar 就会将该对象纳入查询结果中。Filter 不会影响查询结果的排列顺序。\n\n我们通过下方 Collection 作为例子来说明：\n\n```dart\n@collection\nclass Shoe {\n  Id? id;\n\n  int? size;\n\n  late String model;\n\n  late bool isUnisex;\n}\n```\n\n### 查询条件\n\n根据上述 Collection 的字段类型，我们会有以下几种条件表达式可选择：\n\n| 条件                     | 描述                                                                                     |\n| ------------------------ | ---------------------------------------------------------------------------------------- |\n| `.equalTo(value)`        | 匹配等于给定 `value` 的值.                                                               |\n| `.between(lower, upper)` | 匹配介于 `lower` 和 `upper` 之间的值                                                     |\n| `.greaterThan(bound)`    | 匹配大于 `bound` 的值.                                                                   |\n| `.lessThan(bound)`       | 匹配小于 `bound` 的值。 默认情况下 `null` 也会被纳入其中，因为 `null` 被认为小于任何值。 |\n| `.isNull()`              | 匹配为 `null` 的值                                                                       |\n| `.isNotNull()`           | 匹配不为 `null` 的值                                                                     |\n| `.length()`              | 对于数组 List、字符串 String 和关联 Link 的长度查询是基于数组或关联中对象的数量的。      |\n\n假设数据库包含四双鞋的数据，分别为尺码 39、40、46 和一双未知尺码（`null`）。除非你对它们进行排序，不然返回的结果是按照 Id 来排列的。\n\n```dart\n\nisar.shoes.filter()\n  .sizeLessThan(40)\n  .findAll() // -> [39, null]\n\nisar.shoes.filter()\n  .sizeLessThan(40, include: true)\n  .findAll() // -> [39, null, 40]\n\nisar.shoes.filter()\n  .sizeBetween(39, 46, includeLower: false)\n  .findAll() // -> [40, 46]\n\n```\n\n### 逻辑运算符\n\n你可以自行组合下方逻辑运算符来进行查询：\n\n| 运算符     | 描述                                                |\n| ---------- | --------------------------------------------------- |\n| `.and()`   | 如果左右两边的表达式同时为 `true` 则返回 `true`。   |\n| `.or()`    | 如果两侧表达式至少有一个为 `true` 则返回 `true`。   |\n| `.xor()`   | 如果两侧表达式有且只有一个为 `true` 则返回 `true`。 |\n| `.not()`   | 否定随后紧跟表达式的结果。                          |\n| `.group()` | 给条件分组，允许指定运算顺序。                      |\n\n如果你想要查找所有尺码为 46 的鞋子，你可以使用以下代码：\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .findAll();\n```\n\n如果你想要使用多个条件，你可以用逻辑**与** `.and()`、逻辑**或** `.or()` 和逻辑**异或** `.xor()` 来组合多个 Filter。\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeEqualTo(46)\n  .and() // 可选的。 因为 Filter 之间已经隐式使用了逻辑与。\n  .isUnisexEqualTo(true)\n  .findAll();\n```\n\n上述查询条件等同于： `size == 46 && isUnisex == true`。\n\n你也可以通过 `.group()` 对其进行分组：\n\n```dart\nfinal result = await isar.shoes.filter()\n  .sizeBetween(43, 46)\n  .and()\n  .group((q) => q\n    .modelNameContains('Nike')\n    .or()\n    .isUnisexEqualTo(false)\n  )\n  .findAll()\n```\n\n上述查询条件等同于 `size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false)`。\n\n使用逻辑**非** `.not()` 来否定一个条件或一个条件组：\n\n```dart\nfinal result = await isar.shoes.filter()\n  .not().sizeEqualTo(46)\n  .and()\n  .not().isUnisexEqualTo(true)\n  .findAll();\n```\n\n上述查询条件等同于 `size != 46 && isUnisex != true`。\n\n### 字符串条件\n\n除了上述查询条件，还有下表若干个针对字符串查询的条件表达式可供使用。 例如，类似正则的通配符在搜索时提供了更多灵活性。\n\n| 条件                 | 描述                               |\n| -------------------- | ---------------------------------- |\n| `.startsWith(value)` | 匹配以 `value` 开头的字符串。      |\n| `.contains(value)`   | 匹配包含 `value` 的字符串。        |\n| `.endsWith(value)`   | 匹配以 `value` 结尾的字符串。      |\n| `.matches(wildcard)` | 匹配符合 `wildcard` 正则的字符串。 |\n\n**大小写敏感**  \n所有字符串操作都有一个可选的参数 `caseSensitive`，默认情况下为 `true`。\n\n**通配符：**  \n一个[通配符字符串表达式](https://en.wikipedia.org/wiki/Wildcard_character)是指一段使用了两个特殊通配符的普通字符串：\n\n- `*` 通配符匹配零个或多个任意字符。\n- `?` 通配符匹配任意一个字符。\n  例如，通配符字符串 `\"d?g\"` 匹配 `\"dog\"`、`\"dig\"`、和 `\"dug\"`，但不匹配 `\"ding\"`、`\"dg\"` 或`\"a dog\"`。\n\n### 查询修改器\n\n有时候，基于某些特定条件的查询或针对不同值的查询是有必要的。Isar 通过内置强大的修改器功能来实现这些条件查询：\n\n| 修改器                | 描述                                                                                                                  |\n| --------------------- | --------------------------------------------------------------------------------------------------------------------- |\n| `.optional(cond, qb)` | 当且仅当 `condition` 为 `true` 时扩充查询条件。该修改器可被用于查询表达式的任意位置，比如有条件地排序或限制查询个数。 |\n| `.anyOf(list, qb)`    | 为 `values` 中每个值扩充查询条件，然后将它们作逻辑**或**运算。                                                        |\n| `.allOf(list, qb)`    | 为 `values` 中的每个值扩充查询条件，然后将它们作逻辑**与**运算。                                                      |\n\n在下方例子中，我们创建了一个函数，该函数通过一个可选的 Filter 来查找鞋子：\n\n```dart\nFuture<List<Shoe>> findShoes(Id? sizeFilter) {\n  return isar.shoes.filter()\n    .optional(\n      sizeFilter != null, // 当且仅当 sizeFilter != null 时，才会执行 q.sizeEqualTo(sizeFilter!)\n      (q) => q.sizeEqualTo(sizeFilter!),\n    ).findAll();\n}\n```\n\n如果你想要搜寻某些尺码的鞋子时，如 38、40 或 42 码的鞋子，你要么可以使用传统的方式，要么可以使用修改器，代码如下：\n\n```dart\nfinal shoes1 = await isar.shoes.filter()\n  .sizeEqualTo(38)\n  .or()\n  .sizeEqualTo(40)\n  .or()\n  .sizeEqualTo(42)\n  .findAll();\n\nfinal shoes2 = await isar.shoes.filter()\n  .anyOf(\n    [38, 40, 42],\n    (q, int size) => q.sizeEqualTo(size)\n  ).findAll();\n\n// shoes1 == shoes2\n```\n\n当你想要动态查询时，修改器特别有用。\n\n### 数组 List\n\n甚至也可以查询数组 List：\n\n```dart\nclass Tweet {\n  Id? id;\n\n  String? text;\n\n  List<String> hashtags = [];\n}\n```\n\n你可以根据数组的长度来查询：\n\n```dart\nfinal tweetsWithoutHashtags = await isar.tweets.filter()\n  .hashtagsIsEmpty()\n  .findAll();\n\nfinal tweetsWithManyHashtags = await isar.tweets.filter()\n  .hashtagsLengthGreaterThan(5)\n  .findAll();\n```\n\n这分别等同于 `tweets.where((t) => t.hashtags.isEmpty);` 和 `tweets.where((t) => t.hashtags.length > 5);`。 你亦可基于其包含的元素来查询：\n\n```dart\nfinal flutterTweets = await isar.tweets.filter()\n  .hashtagsElementEqualTo('flutter')\n  .findAll();\n```\n\n这等同于 `tweets.where((t) => t.hashtags.contains('flutter'));`。\n\n### 嵌套对象\n\n嵌套对象是 Isar 中最有用的功能之一。可以使用同样适用于顶层对象的查询条件来高效查询它们。假定我们有以下数据模型：\n\n```dart\n@collection\nclass Car {\n  Id? id;\n\n  Brand? brand;\n}\n\n@embedded\nclass Brand {\n  String? name;\n\n  String? country;\n}\n```\n\n我们想要查询品牌名为 `\"BMW\"` 且品牌国家为 `\"Germany\"` 的所有车辆。我们可以执行以下代码：\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q\n    .nameEqualTo('BMW')\n    .and()\n    .countryEqualTo('Germany')\n  ).findAll();\n```\n\n永远试着给嵌套查询分组。上述查询比下方的例子性能更好。尽管查询结果是相同的：\n\n```dart\nfinal germanCars = await isar.cars.filter()\n  .brand((q) => q.nameEqualTo('BMW'))\n  .and()\n  .brand((q) => q.countryEqualTo('Germany'))\n  .findAll();\n```\n\n### 关联（Link）\n\n如果你的数据模型含有[关联或反向关联](links)，你可以根据被关联的对象或被关联对象的数量来进行查询。\n\n:::tip\n记住，关联查询的效率相对更低。因为 Isar 需要查询相关联的对象。考虑尽量使用嵌套对象来代替关联。\n:::\n\n```dart\n@collection\nclass Teacher {\n  Id? id;\n\n  late String subject;\n}\n\n@collection\nclass Student {\n  Id? id;\n\n  late String name;\n\n  final teachers = IsarLinks<Teacher>();\n}\n```\n\n我们想要找到所有修数学或英语的学生：\n\n```dart\nfinal result = await isar.students.filter()\n  .teachers((q) {\n    return q.subjectEqualTo('Math')\n      .or()\n      .subjectEqualTo('English');\n  }).findAll();\n```\n\n只要至少一个相关联对象符合条件，查询条件就会为 `true` 。\n\n让我们搜索所有没有老师的学生：\n\n```dart\nfinal result = await isar.students.filter().teachersLengthEqualTo(0).findAll();\n```\n\n或者：\n\n```dart\nfinal result = await isar.students.filter().teachersIsEmpty().findAll();\n```\n\n## Where 子句\n\nWhere 子句很强大，但是用对可能有点困难。\n\n相对于 Filter， Where 子句利用你在 Schema 中定义的索引来作为查询条件。对索引进行查询比对单条数据查询可快多了。\n\n➡️ 学习更多：[索引](indexes)\n\n:::tip\n一条基本的规则是你应该永远尽可能多地使用 Where 子句来进行索引查询，然后用 Filter 对未被索引的数据进行查询。\n:::\n\n你只能用逻辑**与**来对多个 Where 子句做逻辑运算。换句话说，你可以叠加多个 Where 子句，但不能查询多个 Where 子句的交集。\n\n让我们给下面 Collection 添加索引：\n\n```dart\n@collection\nclass Shoe with IsarObject {\n  Id? id;\n\n  @Index()\n  Id? size;\n\n  late String model;\n\n  @Index(composite: [CompositeIndex('size')])\n  late bool isUnisex;\n}\n```\n\n这里有俩个索引。 `size` 上的索引允许我们使用像 `.sizeEqualTo()` 的 Where 子句，`isUnisex` 上的组合索引则允许我们可以使用像 `isUnisexSizeEqualTo()` 这样的 Where 子句，当然也可以使用 `isUnisexEqualTo()`，因为永远可以使用索引的任何前缀查询语句。\n\n我们可以用组合索引重写之前的查询尺码 46 鞋子的代码。这次查询会比之前快很多：\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexSizeEqualTo(true, 46)\n  .findAll();\n```\n\nWhere 子句还有两个强大特性：它允许你“自由”排序和超快去重操作。\n\n### 将 Where 子句和 Filter 相结合\n\n还记得 `shoes.filter()` 查询吗？实际上它是 `shoes.where().filter()` 的简写。你可以（也应该）在同一查询中同时运用 Where 子句和 Filter 来最大限度地提升查询性能：\n\n```dart\nfinal result = isar.shoes.where()\n  .isUnisexEqualTo(true)\n  .filter()\n  .modelContains('Nike')\n  .findAll();\n```\n\n先用 Where 子句来过滤出部分对象，减少了查询对象数量。然后用 Filter 来查询剩下的对象。\n\n## 排序\n\n你可以在查询中使用 `.sortBy()`、`.sortByDesc()`、 `.thenBy()` 和 `.thenByDesc()` 等方法来给待查询数据进行排序。\n\n下方代码演示了不用索引来查询鞋子，查询结果以鞋款名正序和鞋码倒序来排列：\n\n```dart\nfinal sortedShoes = isar.shoes.filter()\n  .sortByModel()\n  .thenBySizeDesc()\n  .findAll();\n```\n\n对诸多结果进行排序可是非常消耗性能的，尤其是因为排序发生在偏移量（Offset）和限制（Limit）之前。上述排序的方法也从未利用到索引。幸运的是，我们可以再次使用 Where 子句来进行排序以提升性能，这样即使对上百万的结果进行排序也毫无问题。\n\n### 使用 Where 子句来排序\n\n如果你在查询中使用**单个** Where 子句， 那么查询结果就已经通过索引被排列好了。这很重要！\n\n假设我们有鞋码分别为 `[43, 39, 48, 40, 42, 45]` 的鞋子。我们想查询所有鞋码大于 42 的鞋子，然后将它们按鞋码大小排序：\n\n```dart\nfinal bigShoes = isar.shoes.where()\n  .sizeGreaterThan(42) // 也将结果按鞋码大小排序\n  .findAll(); // -> [43, 45, 48]\n```\n\n如你所见，此处结果是按照索引 `size` 来排序的。如果你想要倒序排列，可以将 `sort` 设置为 `Sort.desc`：\n\n```dart\nfinal bigShoesDesc = await isar.shoes.where(sort: Sort.desc)\n  .sizeGreaterThan(42)\n  .findAll(); // -> [48, 45, 43]\n```\n\n有些时候你不想过滤数据，只是想对全部数据排序，但是也可以受益于这种隐式排序。你可以使用 `any` Where 子句：\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .anySize()\n  .findAll(); // -> [39, 40, 42, 43, 45, 48]\n```\n\n如果你使用组合索引，查询结果会根据索引内所有字段进行排序。\n\n:::tip\n如果你需要对结果进行排序，考虑使用索引。尤其是如果你需要用到 `offset()` 和 `limit()`。\n:::\n\n然而有时候使用索引来排序变得不太方便或不容易实现。对于这种情况，你应该尽可能通过索引来减少待查询结果的数量。\n\n## 唯一值\n\n使用 distinct 断言来返回含有唯一值的对象数据。 例如，在 Isar 数据库中找出有多少种不同鞋款：\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .findAll();\n```\n\n你也可以链式地调用多个 distinct 条件来找出所有不同鞋码且不同鞋款的鞋子：\n\n```dart\nfinal shoes = await isar.shoes.filter()\n  .distinctByModel()\n  .distinctBySize()\n  .findAll();\n```\n\n只有每种不同条件组合的第一个对象会被返回。 你可以用 Where 子句和排序操作来控制它。\n\n### Where 子句去重化\n\n如果你有一个索引，它对应的字段可能出现相同值，你可能希望对该字段进行去重化。你可以使用前面部分提到的 `distinctBy` 方法，但它在排序和 Filter 之后执行，所以有些许额外的性能开销。\n\n而如果你只用到一个 Where 子句，你可以只依赖索引来实现去重化。\n\n```dart\nfinal shoes = await isar.shoes.where(distinct: true)\n  .anySize()\n  .findAll();\n```\n\n:::tip\n理论上，你甚至可以使用多个 Where 子句来排序和去重。唯一的限制是那些 Where 子句不能彼此有重叠（即上面提到的交集）且不能使用相同的索引。它们需要按照顺序来使用，以便正确排序。因此如果依赖于这种用法，你必须要细心谨慎。\n:::\n\n## 偏移量（Offset）和限制（Limit）\n\n对于一个懒加载列表组件来说，限制显示的个数通常是很好的办法。你可以使用 `limit()` 对查询结果的数量进行限制：\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .limit(10)\n  .findAll();\n```\n\n而借用 `offset()` 你也可以对查询结果进行分页。\n\n```dart\nfinal firstTenShoes = await isar.shoes.where()\n  .offset(20)\n  .limit(10)\n  .findAll();\n```\n\n因为初始化 Dart 对象往往是执行查询过程中最消耗性能的部分，因此只加载你所需要的对象是一个不错的做法。\n\n## 执行顺序\n\nIsar 总是按照下面顺序执行查询：\n\n1. 遍历索引来查询对象（即使用 Where 子句）\n2. 对对象进行过滤\n3. 对结果进行排序\n4. 去重化（若有）\n5. 偏移量和限制（若有）\n6. 返回查询结果\n\n## 查询操作方法\n\n在之前的例子中，我们使用方法 `.findAll()` 来获取所有匹配对象。然而，还有其他几种查询操作方法：\n\n| 方法             | 描述                                                                                                 |\n| ---------------- | ---------------------------------------------------------------------------------------------------- |\n| `.findFirst()`   | 返回第一个匹配条件的对象，若无匹配，则返回 `null`。                                                  |\n| `.findAll()`     | 返回所有匹配条件的对象。                                                                             |\n| `.count()`       | 返回匹配条件的对象数量。                                                                             |\n| `.deleteFirst()` | 从 Collection 中删除第一个匹配条件的对象。                                                           |\n| `.deleteAll()`   | 从 Collection 中删除所有匹配条件的对象。                                                             |\n| `.build()`       | 将查询条件语句编译，以便重复使用。倘若你想要多次用到同一查询条件，你可以使用这个方法来避免重复代码。 |\n\n## 查询属性\n\n如果你只对单条属性的值感兴趣，你可以使用属性查询。创建一个查询然后选择想要的属性即可：\n\n```dart\nList<String> models = await isar.shoes.where()\n  .modelProperty()\n  .findAll();\n\nList<int> sizes = await isar.shoes.where()\n  .sizeProperty()\n  .findAll();\n```\n\n使用单个属性在反序列化中节省了很多时间。属性查询同样适用于嵌套对象和数组。\n\n## 聚合查询（Aggregation）\n\nIsar 支持对单个属性的聚合查询，为此提供了下表几种聚合查询操作方法：\n\n| 操作         | 描述                                         |\n| ------------ | -------------------------------------------- |\n| `.min()`     | 返回最小值，若无匹配，则返回 `null`。        |\n| `.max()`     | 返回最大值，若无匹配，则返回 `null`。        |\n| `.sum()`     | 返回所有值的总和。                           |\n| `.average()` | 返回所有值的平均值，若无匹配，则返回 `NaN`。 |\n\n直接使用聚合查询比先找出对象，再做聚合运算快多了。\n\n## 动态查询\n\n:::danger\n这部分很大可能与你无关。不鼓励使用动态查询，除非你绝对需要（往往你很少需要）。\n:::\n\n所有上述例子都使用了查询构造器 QueryBuilder 和由 Isar Generator 自动生成的静态扩充方法。你可能想要创建一个动态查询，或自定义的查询语言（就像 Isar Inspector 做的那样）。在这种情况下，你可以使用方法 `buildQuery()`：\n\n| 参数            | 描述                                                                     |\n| --------------- | ------------------------------------------------------------------------ |\n| `whereClauses`  | 查询语句所需的 Where 子句                                                |\n| `whereDistinct` | 是否设置 Where 子句对返回结果去重化（只有在使用单个 Where 子句时有用）。 |\n| `whereSort`     | Where 子句的遍历顺序（只有在使用单个 Where 子句时有用）。                |\n| `filter`        | 用来过滤查询结果的 Filter。                                              |\n| `sortBy`        | 需要用来排序的属性列表。                                                 |\n| `distinctBy`    | 需要用来去重化的属性列表。                                               |\n| `offset`        | 查询结果的偏移量。                                                       |\n| `limit`         | 返回查询结果的最大数量。                                                 |\n| `property`      | 若非空，则只返回该属性的值。                                             |\n\n让我们创建一个动态查询：\n\n```dart\nfinal shoes = await isar.shoes.buildQuery(\n  whereClauses: [\n    WhereClause(\n      indexName: 'size',\n      lower: [42],\n      includeLower: true,\n      upper: [46],\n      includeUpper: true,\n    )\n  ],\n  filter: FilterGroup.and([\n    FilterCondition(\n      type: ConditionType.contains,\n      property: 'model',\n      value: 'nike',\n      caseSensitive: false,\n    ),\n    FilterGroup.not(\n      FilterCondition(\n        type: ConditionType.contains,\n        property: 'model',\n        value: 'adidas',\n        caseSensitive: false,\n      ),\n    ),\n  ]),\n  sortBy: [\n    SortProperty(\n      property: 'model',\n      sort: Sort.desc,\n    )\n  ],\n  offset: 10,\n  limit: 10,\n).findAll();\n```\n\n上述代码等价于以下代码：\n\n```dart\nfinal shoes = await isar.shoes.where()\n  .sizeBetween(42, 46)\n  .filter()\n  .modelContains('nike', caseSensitive: false)\n  .not()\n  .modelContains('adidas', caseSensitive: false)\n  .sortByModelDesc()\n  .offset(10).limit(10)\n  .findAll();\n```\n"
  },
  {
    "path": "docs/docs/zh/recipes/data_migration.md",
    "content": "---\ntitle: 数据迁移\n---\n\n# 数据迁移\n\n当你添加或删除 Collection、或其字段或索引时，Isar 会自动为你的数据库 Schema 做数据迁移。有时候你可能想要自行迁移。Isar 没有提供相关函数，因为这么做会给数据迁移强行加了限制。根据自己的需求来自由实现迁移功能其实很简单。\n\n在下方的例子中，我们希望使用整个数据库的单一版本。我们用 shared_preferences 这个库来保存当下的版本，然后跟我们要迁移的版本做比较。如果两个版本不匹配，那么就迁移数据，更新版本。\n\n:::tip\n你也可以给每个 Collection 分配单独的版本，然后单独为它们各自做数据迁移。\n:::\n\n假设我们有一个用户 Collection，它包含一个出生日 birthday 字段。在我们第二版 App 中，我们需要根据用户年龄来查询用户，就必须添加额外的出生年份字段。\n\n版本 1：\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n}\n```\n\n版本 2：\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  late String name;\n\n  late DateTime birthday;\n\n  short get birthYear => birthday.year;\n}\n```\n\n可问题是现有的用户数据中不会有 `birthYear` 这个字段的信息，因为它在版本 1 中不存在。我们需要借用数据迁移来为 `birthYear` 字段赋值。\n\n```dart\nimport 'package:isar/isar.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nvoid main() async {\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [UserSchema],\n    directory: dir.path,\n  );\n\n  await performMigrationIfNeeded(isar);\n\n  runApp(MyApp(isar: isar));\n}\n\nFuture<void> performMigrationIfNeeded(Isar isar) async {\n  final prefs = await SharedPreferences.getInstance();\n  final currentVersion = prefs.getInt('version') ?? 2;\n  switch(currentVersion) {\n    case 1:\n      await migrateV1ToV2(isar);\n      break;\n    case 2:\n      // 如果版本未设置（新建的时候）或已经是版本 2，我们就不做处理\n      return;\n    default:\n      throw Exception('Unknown version: $currentVersion');\n  }\n\n  // 更新版本\n  await prefs.setInt('version', 2);\n}\n\nFuture<void> migrateV1ToV2(Isar isar) async {\n  final userCount = await isar.users.count();\n\n  // 我们对用户数据进行分页读写，避免同时将所有数据存放到内存\n  for (var i = 0; i < userCount; i += 50) {\n    final users = await isar.users.where().offset(i).limit(50).findAll();\n    await isar.writeTxn((isar) async {\n      // 我们不需要更新任何信息，因为 birthYear 的 getter 已经被使用\n      await isar.users.putAll(users);\n    });\n  }\n}\n```\n\n:::warning\n如果你需要迁移大量数据，考虑在后台使用另一个 isolate 来处理，以防止对 UI 进程造成阻塞。\n:::\n"
  },
  {
    "path": "docs/docs/zh/recipes/full_text_search.md",
    "content": "---\ntitle: 全文检索\n---\n\n# 全文检索\n\n全文检索是一种从数据库中搜索文本的强大功能。你现在应该已经熟悉[索引](../indexes.md)的工作原理了，但还是让我们先了解一些基本知识。\n\n索引就像一张查询表，允许快速地根据给定值查找数据。例如，如果你的对象含有一个 `title` 字段，你可以以该字段创建一张索引表，以此根据给定的标题来快速查询。\n\n## 为什么全文检索很有用？\n\n你本可以轻松通过 Filter 来搜索文本。Isar 为你提供了许多字符串查询方法，例如 `.startsWith()`、`.contains()` 和 `.matches()`。但问题在于 Filter 的复杂度是 `O(n)`，其中 `n` 是 Collection 中对象的个数，像 `.matches()` 这样的字符串操作就格外消耗性能。\n\n:::tip\n全文检索比 Filter 快多了，但是索引也有局限的地方。在本专题中，我们将探寻如何解决这些局限性。\n:::\n\n## 基本示例\n\n想法依然不变：我们对文本中的单词进行索引，而不是对整个文本索引，这样我们可以对单个单词进行搜索。\n\n让我们先创建一个基本的全文检索索引：\n\n```dart\nclass Message {\n  Id? id;\n\n  late String content;\n\n  @Index()\n  List<String> get contentWords => content.split(' ');\n}\n```\n\n现在我们可以通过内容中某些指定词汇来搜索讯息：\n\n```dart\nfinal posts = await isar.messages\n  .where()\n  .contentWordsAnyEqualTo('hello')\n  .findAll();\n```\n\n这条查询非常快，但是有几个问题：\n\n1. 我们只能搜索整个词汇\n2. 我们没考虑标点符号\n3. 我们不支持其他空白字符\n\n## 正确分割文本\n\n让我们完善上述例子。我们可以用一个复杂的正则来正确分割文本，但是在某些少数情况下它很可能会出错且导致查询变得很慢。\n\n[Unicode Annex #29](https://unicode.org/reports/tr29/) 为几乎所有人类语言定义了如何正确分割文本。它很复杂，但是幸运的是，Isar 内部已经帮我们实现了：\n\n```dart\nIsar.splitWords('hello world'); // -> ['hello', 'world']\n\nIsar.splitWords('The quick (“brown”) fox can’t jump 32.3 feet, right?');\n// -> ['The', 'quick', 'brown', 'fox', 'can’t', 'jump', '32.3', 'feet', 'right']\n```\n\n## 我想要更多控制\n\n很简单！我们可以修改索引配置，让它支持前缀匹配和大小写匹配：\n\n```dart\nclass Post {\n  Id? id;\n\n  late String title;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  List<String> get titleWords => title.split(' ');\n}\n```\n\n默认情况下，Isar 会将单词散列化，这么做性能很快且节省存储空间。但是这样就无法使用前缀匹配查询。我们改变了索引类型，使用 `IndexType.value` 而不是 `IndexType.hash`，来直接使用那些单词。借此我们就可以使用 `.titleWordsAnyStartsWith()` 的 Where 子句：\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .titleWordsAnyStartsWith('hel')\n  .or()\n  .titleWordsAnyStartsWith('welco')\n  .or()\n  .titleWordsAnyStartsWith('howd')\n  .findAll();\n```\n\n## 我也需要 `.endsWith()` 方法\n\n没问题！我们会用一个小技巧来实现 `.endsWith()` 匹配：\n\n```dart\nclass Post {\n    Id? id;\n\n    late String title;\n\n    @Index(type: IndexType.value, caseSensitive: false)\n    List<String> get revTitleWords {\n        return Isar.splitWords(title).map(\n          (word) => word.reversed).toList()\n        );\n    }\n}\n```\n\n不要忘记倒序排列查询的结果：\n\n```dart\nfinal posts = await isar.posts\n  .where()\n  .revTitleWordsAnyStartsWith('lcome'.reversed)\n  .findAll();\n```\n\n## 词干提取算法\n\n不幸的是，索引不支持 `.contains()` 匹配（其他数据库也如此）。但是还有几个备选方案值得我们研究一番。选择何种方式完全取决于你的使用场景。举个例子，你可以对词干进行索引，而不是对整个单词索引。\n\n词干提取算法指的是自然语言处理领域里去除词缀得到词根的过程，即得到单词最一般的写法：\n\n```\nconnection\nconnections\nconnective          --->   connect\nconnected\nconnecting\n```\n\n常见的算法有 [Porter 词干提取算法](https://tartarus.org/martin/PorterStemmer/) 和 [Snowball 词干提取算法](https://snowballstem.org/algorithms/)。\n\n还有将单词复杂形态转变成最基础形态的[词形还原](https://en.wikipedia.org/wiki/Lemmatisation)。\n\n## 语音算法\n\n[语音算法](https://en.wikipedia.org/wiki/Phonetic_algorithm) 是指根据发音来检索单词的算法。也就是说，它可以根据发音接近程度来帮你查询结果。\n\n:::warning\n大部分语音算法通常只支持单一语言，一般是英语。\n:::\n\n### Soundex\n\n[Soundex](https://en.wikipedia.org/wiki/Soundex) 是一种语音算法，它通过英文发音来检索名字。它的目的是将同音词用同一编码表示，虽然发音略有差异，但可达到模糊匹配的效果。这是个非常直接明了的算法，也有若干改进版本。\n\n若是用这个算法，那么单词 `\"Robert\"` 和 `\"Rupert\"` 都会返回编码 `\"R163\"`，而单词 `\"Rubin\"` 则返回 `\"R150\"`。 同音词 `\"Ashcraft\"` 和 `\"Ashcroft\"` 则都会返回 `\"A261\"`。\n\n### Double Metaphone\n\n[Double Metaphone](https://en.wikipedia.org/wiki/Metaphone) 也是一种语音算法，是 Metaphone 的二代版本。它在前代基础上改进了不少基本设计。\n\nDouble Metaphone 加入了对大量来自外来语如斯拉夫语、德语、凯尔特语、希腊语、法语、意大利语、西班牙语、中文等的不规则英文单词发音的支持。\n"
  },
  {
    "path": "docs/docs/zh/recipes/multi_isolate.md",
    "content": "---\ntitle: Multi-Isolate 用法\n---\n\n# Multi-Isolate 用法\n\n所有的 Dart 代码都是在 isolate 而不是线程中运行的。每个 isolate 都有它自己的内存，确保彼此之间互相隔离。\n\nIsar 可同时用于多个 isolate，甚至观察者也支持跨多个 isolate。本专题将会探讨如何在多 isolate 环境下使用 Isar。\n\n## 什么时候使用多个 isolate\n\nIsar 事务即使在单 isolate 环境中也是并行运行的。但有些情况下，从多 isolate 环境访问 Isar 可能依然利大于弊。\n\n因为 Isar 花费不少时间去对 Dart 对象进行编码和解码。你姑且可以将之类比成对 JSON 编码和解码（只不过实际性能更快）。这些操作都在读取数据的 isolate 中进行，当然会阻碍该 isolate 中其他代码的运行。也就是说：Isar 的确需要在你的 isolate 中做不少工作。\n\n如果你同时需要读取或写入几百个对象数据，在 UI isolate 中这么做没有问题。但是如果 UI 已经很忙碌或事务操作量很大，你就应该考虑使用多个 isolate。\n\n## 例子\n\n首先我们在新的 isolate 中创建 Isar 实例。因为在主 isolate 中我们已经有一个 Isar 实例，所以调用方法 `Isar.open()` 会直接返回该实例。\n\n:::warning\n确保在主 isolate 中给实例传入相同的 Schema，否则会发生错误。\n:::\n\n`compute()` 创建一个新的 isolate，并运行传给它的函数。\n\n```dart\nvoid main() {\n  // 在 UI isolate 中创建 Isar 实例\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [MessageSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  // 订阅数据库中消息表的变化\n  isar.messages.watchLazy(() {\n    print('omg the messages changed!');\n  });\n\n  // 创建一个新的 isolate，写入 10000 条讯息到数据库\n  compute(createDummyMessages, 10000).then(() {\n    print('isolate finished');\n  });\n\n  // 一段时间后，打印出：\n  // > omg the messages changed!\n  // > isolate finished\n}\n\n// 函数将会在新的 isolate 中被执行\nFuture createDummyMessages(int count) async {\n  // 我们没必要在此指定路径，因为它已经被创建好了\n  final dir = await getApplicationDocumentsDirectory();\n  \n  final isar = await Isar.open(\n    [PostSchema],\n    directory: dir.path,\n    name: 'myInstance',\n  );\n\n  final messages = List.generate(count, (i) => Message()..content = 'Message $i');\n  // 我们在 isolate 中使用了同步事务\n  isar.writeTxnSync(() {\n    isar.messages.insertAllSync(messages);\n  });\n}\n```\n\n上述例子中需要注意的几个点：\n\n- `isar.messages.watchLazy()` 在 UI isolate 中被调用，接收来自另一个 isolate 中数据变化的通知。\n- 实例之间用名称来辨别。默认实例名称为 `default`，但是在上面的示例中，我们将它命名为 `myInstance`。\n- 我们在一个新 isolate 中使用了同步事务来写入讯息。阻塞这个 isolate 没什么问题，因为它不会影响到 UI isolate，而且同步事务相比异步更快。\n"
  },
  {
    "path": "docs/docs/zh/recipes/string_ids.md",
    "content": "---\ntitle: 字符串 Id\n---\n\n# 字符串 Id\n\n这是我遇到的最常见的请求之一，所以就有了这篇教程。\n\nIsar 原生不支持字符串 Id，这是经过深思熟虑的：原因是整型 Id 比字符串 Id 性能更好。尤其是在处理关联上，使用字符串 Id 会显著增加额外的性能开销。\n\n我理解，有时候你需要存储一些使用 UUID 或非整型 Id 的数据。我建议将这些字符串 Id 作为对象的属性，并用其快速散列化后的 64 位整型作为 Isar 对象的 Id。\n\n```dart\n@collection\nclass User {\n  String? id;\n\n  Id get isarId => fastHash(id!);\n\n  String? name;\n\n  int? age;\n}\n```\n\n通过这个办法，你既可以高效地使用整型 Id 来处理关联，又保留了原有数据中的字符串 Id。\n\n## 快速散列函数\n\n理想情况下，你的散列函数应该兼具高可用性（没人希望崩溃或意外发生）和高性能。我推荐使用下方代码实现：\n\n```dart\n/// 针对 Dart 字符串优化的 64 位哈希算法 FNV-1a\nint fastHash(String string) {\n  var hash = 0xcbf29ce484222325;\n\n  var i = 0;\n  while (i < string.length) {\n    final codeUnit = string.codeUnitAt(i++);\n    hash ^= codeUnit >> 8;\n    hash *= 0x100000001b3;\n    hash ^= codeUnit & 0xFF;\n    hash *= 0x100000001b3;\n  }\n\n  return hash;\n}\n```\n\n如果你选择其他散列函数，确保它返回 64 位整型，避免使用加密散列函数，因为它们非常慢。\n\n:::warning\n避免使用 `string.hashCode`，因为无法保证它能够适用于各个平台，或适配各个版本的 Dart。\n:::\n"
  },
  {
    "path": "docs/docs/zh/schema.md",
    "content": "---\ntitle: Schema\n---\n\n# Schema\n\n当你使用 Isar 来存储数据时，你需要对 Collection 进行操作。Collection 可理解为 Isar 数据库中的表，其包含的数据只能为同一类 Dart 对象。每个对象则代表了对应数据表中的一行数据。\n\n对 Collection 的定义就被称为 “Schema”。 Isar Generator 会根据 Schema 自动生成大部分代码，然后你可以通过这些代码来对 Collection 进行相关操作。\n\n## Collection 的构造\n\n你可以通过给每一个类添加 `@collection` 或 `@Collection()` 的注解来定义一个 Collection。 一个 Collection 所包含的字段对应数据库中的每一列，其中包括一个主 key。\n\n如下代码所示，`User` Collection 表示一张用户数据表，其列名分别为 id、firsName 以及 lastName：\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n:::tip\n为了保存一个字段，Isar 必须能够读取到它。你可以声明其为公开字段，或者为其提供 getter 或 setter 方法，来确保 Isar 能够读取到它。\n:::\n\n在定义 Collection 的时候，若干配置参数可供选择：\n\n| 参数          | 描述                                                                                                                   |\n| ------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| `inheritance` | 确定 Isar 是否保存父类的字段或 mixin。默认情况为启用。                                                                 |\n| `accessor`    | 允许你更改默认的 Collection 的访问名。例如，默认设置下自动生成的代码会用 `isar.contacts` 来访问 `Contact` Collection。 |\n| `ignore`      | 允许忽视特定字段。 这同样也适用于超类。                                                                                |\n\n### Isar Id\n\n每一个 Collection 类都必须定义一个 `Id` 类型的 Id 属性，以便唯一指代一个对象。 实际上，`Id` 类型在这里只是 `int` 类型的别名，只不过是为了让 Isar Generator 能够识别该属性。\n\nIsar 将会自动索引 Id 属性，这能够让你轻松高效地通过对象的 Id 来查询或修改它。\n\n你可以选择要么自己设置 Id，要么让 Isar 自行分配一个自增的 Id。但是如果你设置的 `id` 字段为 `null` 或者不是 `final`，Isar 也会自动覆盖成自增的 Id。倘若你想要一个非空自增的 Id，那么你可以给它赋值为 `Isar.autoIncrement`，而不是 `null`。\n\n:::tip\n当对象被删除后，其自增的 Id 也不会被重新使用。唯一重置 Id 的方法是删除整个数据库。\n:::\n\n### 给 Collection 和其字段改名\n\n默认情况下，Isar 会使用类的名称作为 Collection 的名称。相似地，Isar 也会用字段名称来作为数据表的列名。倘若你想要修改 Collection 或字段的名称，在对应位置添加 `@Name` 注解。可参考下方例子：\n\n```dart\n@collection\n@Name(\"User\")\nclass MyUserClass1 {\n\n  @Name(\"id\")\n  Id myObjectId;\n\n  @Name(\"firstName\")\n  String theFirstName;\n\n  @Name(\"lastName\")\n  String familyNameOrWhatever;\n}\n```\n\n尤其是当你想要修改已经存入数据库中对象的字段名称时，你可以考虑使用 `@Name`（例如，现有数据字段命名带有下划线，而在 Dart 中定义时则为小写驼峰式，如此情况下可以通过修改名称来匹配）。否则的话， 因为名称不匹配，导致原有的数据未被保存，而不同名的字段则额外被添加到数据库里。\n\n### 忽略字段\n\nIsar 会保存 Collection 类中所有的公开属性。如下例子所示，给一个属性或 getter 添加 `@ignore` 注解，就可以防止其被 Isar 保存：\n\n```dart\n@collection\nclass User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n\n  @ignore\n  String? password;\n}\n```\n\n当 Collection 类从父类继承了一些你不想保存的字段时，通常直接在 `@Collection` 注解里设置 `ignore` 更为便利：\n\n```dart\n@collection\nclass User {\n  Image? profilePicture;\n}\n\n@Collection(ignore: {'profilePicture'})\nclass Member extends User {\n  Id? id;\n\n  String? firstName;\n\n  String? lastName;\n}\n```\n\n如果一个 Collection 包含 Isar 不支持的数据类型的字段时，你必须忽略掉对应字段。\n\n:::warning\n记住，在那些未被 Isar 保存的字段里存储信息不是正确的做法。\n:::\n\n## Isar 支持的数据类型\n\nIsar 支持以下数据类型：\n\n- `bool`\n- `byte`\n- `short`\n- `int`\n- `float`\n- `double`\n- `DateTime`\n- `String`\n- `List<bool>`\n- `List<byte>`\n- `List<short>`\n- `List<int>`\n- `List<float>`\n- `List<double>`\n- `List<DateTime>`\n- `List<String>`\n\n还有，嵌套的对象和枚举也是支持的。我们等会会讲到它们。\n\n## byte，short，float\n\n对于大多数应用场景，你不会需要整个 64 位范围的整数或双精度浮点数。Isar 支持以下额外的数据类型，它们可以用于较小范围的数字，这样也帮助你节省了存储空间和内存使用。\n\n| 类型       | 字节大小 | 数字范围                                                |\n| ---------- | -------- | ------------------------------------------------------- |\n| **byte**   | 1        | 0 到 255                                                |\n| **short**  | 4        | -2,147,483,647 到 2,147,483,647                         |\n| **int**    | 8        | -9,223,372,036,854,775,807 到 9,223,372,036,854,775,807 |\n| **float**  | 4        | -3.4e38 到 3.4e38                                       |\n| **double** | 8        | -1.7e308 到 1.7e308                                     |\n\n表中的数字类型只是原生 Dart 数据类型的别名。所以例如 `short` 实际上和 `int` 用法一样，只不过限定了它的数字范围。\n\n参考下方例子：\n\n```dart\n@collection\nclass TestCollection {\n  Id? id;\n\n  late byte byteValue;\n\n  short? shortValue;\n\n  int? intValue;\n\n  float? floatValue;\n\n  double? doubleValue;\n}\n```\n\n所有的数字类型也可以用于数组 List。比如你可以用 `List<byte>` 来保存 byte。\n\n## 可空类型\n\n理解 Isar 中的可空性原理是最基本的：数字类型**并没有**对于 `null` 的专门表达。相反，Isar 用特定的值来表示空：\n\n| 类型       | VM            |\n| ---------- | ------------- |\n| **short**  | `-2147483648` |\n| **int**    |  `int.MIN`    |\n| **float**  | `double.NaN`  |\n| **double** |  `double.NaN` |\n\n`bool`、`String` 和 `List` 则有对 `null` 的表达。\n\n这样的处理方式能够带来性能上的提高，能够让你自由更改字段的可空性，而不需要额外的数据迁移或多余代码来处理空值。\n\n:::warning\n`byte` 类型不支持空值。\n:::\n\n## 日期（DateTime）\n\nIsar 不会保存日期类型数据中的时区信息。相反，它会在存储之前将 `DateTime` 数据转成 UTC 格式。 Isar 返回的日期数据都为当地时间。\n\n`DateTime` 以微秒精度被存储，而在浏览器中，由于 JavaScript 的局限性，最高只能以毫秒精度被存储。\n\n## 枚举（Enum）\n\n就像其他 Isar 所支持的数据类型一样，Isar 也允许存储和使用枚举类型。然而，你可以选择 Isar 如何来表示枚举。 Isar 支持以下四种不同策略：\n\n| 策略类型    | 描述                                                         |\n| ----------- | ------------------------------------------------------------ |\n| `ordinal`   | 枚举的索引以 `byte` 类型被保存。性能很高但不支持可空的枚举。 |\n| `ordinal32` | 枚举的索引以 `short` (4 字节整型) 被保存。                   |\n| `name`      | 枚举的名称以 `String` 被保存。                               |\n| `value`     | 用一个自定义属性来读取枚举值。                               |\n\n:::warning\n`ordinal` 和 `ordinal32` 依赖于枚举值的属性。如果你改变了枚举内值的顺序，现有数据库将会返回错误的结果。\n:::\n\n让我们通过例子来了解每种策略。\n\n```dart\n@collection\nclass EnumCollection {\n  Id? id;\n\n  @enumerated // 等价于 EnumType.ordinal\n  late TestEnum byteIndex; // 不能为空值\n\n  @Enumerated(EnumType.ordinal)\n  late TestEnum byteIndex2; // 不能为空值\n\n  @Enumerated(EnumType.ordinal32)\n  TestEnum? shortIndex;\n\n  @Enumerated(EnumType.name)\n  TestEnum? name;\n\n  @Enumerated(EnumType.value, 'myValue')\n  TestEnum? myValue;\n}\n\nenum TestEnum {\n  first(10),\n  second(100),\n  third(1000);\n\n  const TestEnum(this.myValue);\n\n  final short myValue;\n}\n```\n\n当然，枚举类型也可以被用于数组 List。\n\n## 嵌套对象\n\n在 Collection 中使用嵌套对象通常很有用。对象可以嵌套到任何深度。但是请记住，对一个嵌套对象进行修改需要将整个对象树写入数据库。\n\n```dart\n@collection\nclass Email {\n  Id? id;\n\n  String? title;\n\n  Recepient? recipient;\n}\n\n@embedded\nclass Recepient {\n  String? name;\n\n  String? address;\n}\n```\n\n嵌套对象可为空且可以扩展自其他对象，唯一的要求是需要添加 `@embedded` 注解，而且它们的构造器不能有参数。\n"
  },
  {
    "path": "docs/docs/zh/transactions.md",
    "content": "---\ntitle: 事务\n---\n\n# 事务（Transaction）\n\n在 Isar 中，事务将多条数据库操作序列合并成单个逻辑单位。大多数与 Isar 的交互都隐式用到了事务。Isar 的读写操作是兼容 [ACID](http://en.wikipedia.org/wiki/ACID) 特性的。倘若错误发生，事务会自动回滚。\n\n## 显式事务\n\n在显式事务中，你会得到连续的数据库快照。尝试缩短事务的持续时长。禁止在事务中访问网络或做其他需长时间运行的操作。\n\n事务（特别是写入事务）的确有性能损耗，你应该尽可能将连续的操作序列并入到单一事务。\n\n事务要么是同步的，要么是异步的。在同步事务中，你只能使用同步操作。类似地，在异步事务中只能使用异步操作。\n\n|      | 读取         | 读写              |\n| ---- | ------------ | ----------------- |\n| 同步 | `.txnSync()` | `.writeTxnSync()` |\n| 异步 | `.txn()`     | `.writeTxn()`     |\n\n### 读取事务\n\n显式的读取事务是可选的，但是它们可以让你进行原子化读取并且依赖于事务执行过程中数据库的一致性。对于所有的读取操作，Isar 内部总是使用隐式的读取事务。\n\n:::tip\n异步的读取事务和其他读写事务是并行运行的。很酷，对吧？\n:::\n\n### 写入事务\n\n不同于读取事务，在 Isar 中必须显式使用写入事务。\n\n当一个写入事务成功完成后，它会自动提交，将所有修改写入到磁盘。如果有错误发生，事务就会被终止，所有修改会被回滚。事务就是“应用所有修改或什么都不修改”：在一个成功执行的事务里执行所有修改，或什么都不修改来保证数据的一致性。\n\n:::warning\n当数据操作失败时，该事务会被终止。即使你在 Dart 中捕获到了错误，也不要再次使用该事务。\n:::\n\n```dart\n@collection\nclass Contact {\n  Id? id;\n\n  String? name;\n}\n\n// 良好\nawait isar.writeTxn(() async {\n  for (var contact in getContacts()) {\n    await isar.contacts.put(contact);\n  }\n});\n\n// 不好：要将循环放到事务里面\nfor (var contact in getContacts()) {\n  await isar.writeTxn(() async {\n    await isar.contacts.put(contact);\n  });\n}\n```\n"
  },
  {
    "path": "docs/docs/zh/tutorials/quickstart.md",
    "content": "---\ntitle: 快速开始\n---\n\n# 快速开始\n\n嗨，你可终于来啦！让我们开始使用 Flutter 生态中最酷的数据库吧...\n\n废话不多说，让我们来看代码。\n\n## 1. 添加依赖\n\n在开始之前，我们需要在 `pubspec.yaml` 文件中添加若干依赖，可以运行以下命令帮助我们完成：\n\n```bash\ndart pub add isar:^0.0.0-placeholder isar_flutter_libs:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\ndart pub add dev:isar_generator:^0.0.0-placeholder --hosted-url=https://pub.isar-community.dev\n```\n\n## 2. 给类添加注解\n\n用 `@collection` 给你的 Collection 类添加注解，并指定一个 `Id` 字段。\n\n```dart\npart 'user.g.dart';\n\n@collection\nclass User {\n  Id id = Isar.autoIncrement; // 你也可以用 id = null 来表示 id 是自增的\n\n  String? name;\n\n  int? age;\n}\n```\n\nId 唯一指向了 Collection 中的对象，之后我们可通过 Id 来查询这些对象。\n\n## 3. 运行代码生成器\n\n对于纯 Dart 项目，通过以下命令来执行 `build_runner`：\n\n```\ndart run build_runner build\n```\n\n倘若你的项目用到了 Flutter，可用下方命令来代替：\n\n```\nflutter pub run build_runner build\n```\n\n## 4. 创建一个 Isar 实例\n\n创建一个新的 Isar 实例，并将你想保存到 Isar 的所有 collection 的 schema（它在上一步由 Isar Generator 根据你定义的 collection 自动生成） 作为参数传入。你还可以指定实例的名称以及它所存储数据的文件路径。\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [UserSchema],\n  directory: dir.path,\n);\n```\n\n## 5. 读写操作\n\n当实例被创建后，我们就可以使用它了。\n\n可以通过 `IsarCollection` 来调用所有 CRUD 方法。\n\n```dart\nfinal newUser = User()..name = 'Jane Doe'..age = 36;\n\nawait isar.writeTxn(() async {\n  await isar.users.put(newUser); // 将新用户数据写入到 Isar\n});\n\nfinal existingUser = await isar.users.get(newUser.id); // 通过 Id 读取用户数据\n\nawait isar.writeTxn(() async {\n  await isar.users.delete(existingUser.id!); // 通过 Id 删除指定用户\n});\n```\n\n## 其他资源\n\n你倾向于通过视频来学习？不妨查看下方一些资源吧：\n\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/CwC9-a9hJv4\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/videoseries?list=PLKKf8l1ne4_hMBtRykh9GCC4MMyteUTyf\" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n<br>\n<div class=\"video-block\">\n  <iframe max-width=100% height=auto src=\"https://www.youtube.com/embed/pdKb8HLCXOA \" title=\"Isar Database\" frameborder=\"0\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</div>\n"
  },
  {
    "path": "docs/docs/zh/watchers.md",
    "content": "---\ntitle: 观察者\n---\n\n# 观察者（Watcher）\n\nIsar 允许你订阅数据库中的变化。你可以“观察”单个对象、整个 Collection 或单个查询的变化。\n\n观察者可以让你针对数据库中的变化高效地做出回应。例如，当一个联系人被添加之后，你可以重建 UI；或当一个文件被修改后，发送一个网络请求等等。\n\n当事务成功被执行后，目标被修改，然后就会通知观察者。\n\n## 观察对象\n\n如果你想在某个对象被创建、修改或删除时收到通知，你可以通过下面代码观察该对象：\n\n```dart\nStream<User> userChanged = isar.users.watchObject(5);\nuserChanged.listen((newUser) {\n  print('User changed: ${newUser?.name}');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// 打印出：User changed: David\n\nfinal user2 = User(id: 5)..name = 'Mark';\nawait isar.users.put(user);\n// 打印出：User changed: Mark\n\nawait isar.users.delete(5);\n// 打印出：User changed: null\n```\n\n如你所见，声明观察者的时候，Id 为 5 的对象还未被创建。一旦被创建，观察者就会收到通知。\n\n还有一个参数 `fireImmediately`，如果你设置为 `true`，Isar 会立刻将该对象的当前值添加到 Stream 中。\n\n### 懒观察\n\n也许你不需要知道一个对象的最新值，只需了解其是否被修改过，那么可以用懒观察，这样 Isar 无需去读取该对象的值：\n\n```dart\nStream<void> userChanged = isar.users.watchObjectLazy(5);\nuserChanged.listen(() {\n  print('User 5 changed');\n});\n\nfinal user = User(id: 5)..name = 'David';\nawait isar.users.put(user);\n// 打印出：User 5 changed\n```\n\n## 观察 Collection\n\n除了观察单个对象，你也可以观察整个 Collection 中是否有对象被添加、修改或删除：\n\n```dart\nStream<void> userChanged = isar.users.watchLazy();\nuserChanged.listen(() {\n  print('A User changed');\n});\n\nfinal user = User()..name = 'David';\nawait isar.users.put(user);\n// 打印出： A User changed\n```\n\n## 观察查询\n\n甚至你也可以观察整个查询的结果是否发生变化。Isar 尽力只在查询结果真正发生变化时通知你。但是当由关联造成查询结果发生变化时，你不会收到任何通知。针对关联变化，你可以观察 Collection。\n\n```dart\nQuery<User> usersWithA = isar.users.filter()\n    .nameStartsWith('A')\n    .build();\n\nStream<List<User>> queryChanged = usersWithA.watch(fireImmediately: true);\nqueryChanged.listen((users) {\n  print('Users with A are: $users');\n});\n// 打印出：Users with A are: []\n\nawait isar.users.put(User()..name = 'Albert');\n// 打印出：Users with A are: [User(name: Albert)]\n\nawait isar.users.put(User()..name = 'Monika');\n// 无任何打印输出\n\nawait isar.users.put(User()..name = 'Antonia');\n// 打印出：Users with A are: [User(name: Albert), User(name: Antonia)]\n```\n\n:::warning\n如果你的查询使用了偏移量和限制，或者进行了去重化，即使是在查询结果范围之外的对象，若符合该查询条件，Isar 也会通知你查询结果发生了变化。\n:::\n\n就像观察对象的懒观察一样，你也可以使用 `watchLazy()` 来懒观察一条查询结果是否有变化，而无需去读取查询的结果。\n\n:::danger\n观察查询时返回每一个变动是十分低效的。尽量使用懒观察 Collection 来代替。\n:::\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"name\": \"docs\",\n  \"version\": \"1.0.0\",\n  \"devDependencies\": {\n    \"@vuepress/plugin-shiki\": \"^2.0.0-beta.53\",\n    \"vuepress\": \"^2.0.0-beta.53\"\n  },\n  \"scripts\": {\n    \"dev\": \"vuepress dev docs\",\n    \"build\": \"vuepress build docs\"\n  }\n}"
  },
  {
    "path": "examples/pub/.gitignore",
    "content": "*.g.dart\n\nandroid/\nios/\nwindows/\nlinux/\nmacos/\nweb/"
  },
  {
    "path": "examples/pub/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n    - platform: android\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n    - platform: ios\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n    - platform: linux\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n    - platform: macos\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n    - platform: web\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n    - platform: windows\n      create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n      base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "examples/pub/README.md",
    "content": "# pub\n\nSample showcasing the use of Isar to build a fully offline-first pub.dev client."
  },
  {
    "path": "examples/pub/analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\n\nanalyzer:\n  errors:\n    public_member_api_docs: ignore"
  },
  {
    "path": "examples/pub/lib/asset_loader.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:dio/dio.dart';\nimport 'package:isar/isar.dart';\nimport 'package:pub_app/models/asset.dart';\nimport 'package:pub_app/models/package.dart';\nimport 'package:pub_app/repository.dart';\nimport 'package:tar/tar.dart';\n\nclass PackageAndVersion {\n  PackageAndVersion(this.package, this.version);\n\n  final String package;\n  final String version;\n}\n\nFuture<void> loadAssets(PackageAndVersion p) async {\n  final isar = Isar.openSync(\n    [PackageSchema, AssetSchema],\n    inspector: false,\n  );\n\n  Asset? readme;\n  Asset? changelog;\n\n  final targz = await Repository(Dio()).downloadPackage(p.package, p.version);\n  final tar = gzip.decode(targz);\n\n  final reader = TarReader(Stream.value(tar));\n  while (await reader.moveNext()) {\n    final entry = reader.current;\n\n    if (entry.type == TypeFlag.reg) {\n      if (readme == null && entry.name.toLowerCase() == 'readme.md') {\n        final content = await entry.contents.transform(utf8.decoder).join();\n        readme = Asset(\n          package: p.package,\n          version: p.version,\n          kind: AssetKind.readme,\n          content: content,\n        );\n      } else if (changelog == null &&\n          entry.name.toLowerCase() == 'changelog.md') {\n        final content = await entry.contents.transform(utf8.decoder).join();\n        changelog = Asset(\n          package: p.package,\n          version: p.version,\n          kind: AssetKind.changelog,\n          content: content,\n        );\n      }\n    }\n\n    if (readme != null && changelog != null) {\n      break;\n    }\n  }\n\n  if (readme != null || changelog != null) {\n    isar.writeTxnSync(() {\n      isar.assets.putAllSync([\n        if (readme != null) readme,\n        if (changelog != null) changelog,\n      ]);\n    });\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/main.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:pub_app/ui/detail_page.dart';\nimport 'package:pub_app/ui/home_page.dart';\nimport 'package:pub_app/ui/search_page.dart';\n\nvoid main() {\n  runApp(ProviderScope(child: PubApp()));\n}\n\nfinal darkModePod = StateProvider((ref) => false);\n\nclass PubApp extends ConsumerWidget {\n  PubApp({super.key});\n\n  final _router = GoRouter(\n    routes: [\n      GoRoute(\n        path: '/',\n        builder: (context, state) => const HomePage(),\n      ),\n      GoRoute(\n        path: '/packages/:package',\n        builder: (context, state) => DetailPage(\n          name: state.params['package']!,\n        ),\n      ),\n      GoRoute(\n        path: '/packages/:package/versions/:version',\n        builder: (context, state) => DetailPage(\n          name: state.params['package']!,\n          version: state.params['version'],\n        ),\n      ),\n      GoRoute(\n        path: '/search/:query',\n        builder: (context, state) => SearchPage(\n          query: state.params['query']!,\n        ),\n      ),\n    ],\n  );\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final darkMode = ref.watch(darkModePod);\n    return MaterialApp.router(\n      routeInformationProvider: _router.routeInformationProvider,\n      routeInformationParser: _router.routeInformationParser,\n      routerDelegate: _router.routerDelegate,\n      title: 'Pub',\n      theme: ThemeData.from(\n        colorScheme: ColorScheme.fromSeed(\n          seedColor: const Color(0xFF1c2834),\n          brightness: darkMode ? Brightness.dark : Brightness.light,\n        ),\n        useMaterial3: true,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/models/api/metrics.dart",
    "content": "import 'package:json_annotation/json_annotation.dart';\n\npart 'metrics.g.dart';\n\n@JsonSerializable(createToJson: false)\nclass ApiPackageMetrics {\n  ApiPackageMetrics({\n    required this.grantedPoints,\n    required this.maxPoints,\n    required this.likeCount,\n    required this.popularityScore,\n    required this.tags,\n  });\n\n  factory ApiPackageMetrics.fromJson(Map<String, Object?> json) =>\n      _$ApiPackageMetricsFromJson(json);\n\n  final int grantedPoints;\n\n  final int maxPoints;\n\n  final int likeCount;\n\n  final double popularityScore;\n\n  final List<String> tags;\n}\n"
  },
  {
    "path": "examples/pub/lib/models/api/package.dart",
    "content": "import 'package:json_annotation/json_annotation.dart';\nimport 'package:pubspec/pubspec.dart';\n\npart 'package.g.dart';\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\nclass ApiPackage {\n  ApiPackage({\n    required this.name,\n    required this.latest,\n    this.versions = const [],\n  });\n\n  factory ApiPackage.fromJson(Map<String, dynamic> json) =>\n      _$ApiPackageFromJson(json);\n\n  final String name;\n\n  final ApiPackageVersion latest;\n\n  final List<ApiPackageVersion> versions;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\nclass ApiPackageVersion {\n  ApiPackageVersion({\n    required this.version,\n    required this.pubspec,\n    required this.published,\n  });\n\n  factory ApiPackageVersion.fromJson(Map<String, dynamic> json) =>\n      _$ApiPackageVersionFromJson(json);\n\n  final String version;\n\n  final PubSpec pubspec;\n\n  final DateTime published;\n}\n"
  },
  {
    "path": "examples/pub/lib/models/asset.dart",
    "content": "import 'package:isar/isar.dart';\n\npart 'asset.g.dart';\n\n@collection\nclass Asset {\n  Asset({\n    required this.package,\n    required this.version,\n    required this.kind,\n    required this.content,\n  }) : id = Isar.autoIncrement;\n\n  Id id;\n\n  @Index(\n    unique: true,\n    replace: true,\n    composite: [\n      CompositeIndex('version'),\n      CompositeIndex('kind'),\n    ],\n  )\n  final String package;\n\n  final String version;\n\n  @enumerated\n  final AssetKind kind;\n\n  final String content;\n}\n\nenum AssetKind { readme, changelog }\n"
  },
  {
    "path": "examples/pub/lib/models/package.dart",
    "content": "import 'package:copy_with_extension/copy_with_extension.dart';\nimport 'package:isar/isar.dart';\nimport 'package:pub_app/models/api/metrics.dart';\nimport 'package:pub_app/models/api/package.dart';\nimport 'package:pubspec/pubspec.dart';\n\npart 'package.g.dart';\n\n@CopyWith()\n@collection\nclass Package {\n  Package({\n    required this.name,\n    required this.version,\n    required this.isLatest,\n    this.homepage,\n    this.documentation,\n    this.description,\n    required this.dependencies,\n    required this.devDependencies,\n    required this.published,\n    this.points,\n    this.likes,\n    this.popularity,\n    this.publisher,\n    this.dart,\n    this.flutter,\n    this.flutterFavorite,\n    this.license,\n    this.osiLicense,\n    this.platforms,\n  }) : id = Isar.autoIncrement;\n\n  final Id id;\n\n  @Index(unique: true, replace: true, composite: [CompositeIndex('version')])\n  final String name;\n\n  final String version;\n\n  final bool isLatest;\n\n  final String? description;\n\n  final String? homepage;\n\n  final String? documentation;\n\n  final List<Dependency> dependencies;\n\n  final List<Dependency> devDependencies;\n\n  final DateTime published;\n\n  final short? points;\n\n  final short? likes;\n\n  final float? popularity;\n\n  final String? publisher;\n\n  final bool? dart;\n\n  final bool? flutter;\n\n  final bool? flutterFavorite;\n\n  final String? license;\n\n  final bool? osiLicense;\n\n  @enumerated\n  final List<SupportedPlatform>? platforms;\n\n  static List<Package> fromApiPackage(ApiPackage package) {\n    final latestVersion = package.latest.version;\n    final versions = <Package>[];\n    for (final p in package.versions) {\n      versions.add(\n        Package(\n          name: package.name,\n          version: p.version,\n          isLatest: p.version == latestVersion,\n          homepage: p.pubspec.homepage,\n          documentation: p.pubspec.documentation,\n          description: p.pubspec.description,\n          dependencies: Dependency.fromDependencies(p.pubspec.dependencies),\n          devDependencies:\n              Dependency.fromDependencies(p.pubspec.devDependencies),\n          published: p.published,\n        ),\n      );\n    }\n\n    return versions;\n  }\n\n  Package copyWithMetrics(ApiPackageMetrics metrics) {\n    final publishers =\n        metrics.tags.where((t) => t.startsWith('publisher:')).toList();\n    final publisher =\n        publishers.isNotEmpty ? publishers.first.substring(10) : null;\n    return copyWith(\n      points: metrics.grantedPoints,\n      likes: metrics.likeCount,\n      popularity: metrics.popularityScore,\n      publisher: publisher,\n      dart: metrics.tags.contains('sdk:dart'),\n      flutter: metrics.tags.contains('sdk:flutter'),\n      flutterFavorite: metrics.tags.contains('is:flutter-favorite'),\n      license: metrics.tags\n          .firstWhere(\n            (e) =>\n                e.startsWith('license:') &&\n                e != 'license:osi-approved' &&\n                e != 'license:fsf-libre',\n            orElse: () => 'license:unknown',\n          )\n          .substring(8)\n          .toUpperCase(),\n      osiLicense: metrics.tags.contains('license:osi-approved'),\n      platforms: [\n        if (metrics.tags.contains('platform:web')) SupportedPlatform.web,\n        if (metrics.tags.contains('platform:android'))\n          SupportedPlatform.android,\n        if (metrics.tags.contains('platform:ios')) SupportedPlatform.ios,\n        if (metrics.tags.contains('platform:linux')) SupportedPlatform.linux,\n        if (metrics.tags.contains('platform:macos')) SupportedPlatform.macos,\n        if (metrics.tags.contains('platform:windows'))\n          SupportedPlatform.windows,\n      ],\n    );\n  }\n}\n\n@embedded\nclass Dependency {\n  Dependency({this.name = 'unknown', this.constraint = 'any'});\n\n  final String name;\n\n  final String constraint;\n\n  static List<Dependency> fromDependencies(\n    Map<String, DependencyReference> dependenciesMap,\n  ) {\n    final dependencies = <Dependency>[];\n    for (final package in dependenciesMap.keys) {\n      final dep = dependenciesMap[package]!;\n      final constraint =\n          dep is HostedReference ? dep.versionConstraint.toString() : 'unknown';\n      dependencies.add(\n        Dependency(\n          name: package,\n          constraint: constraint,\n        ),\n      );\n    }\n\n    return dependencies;\n  }\n}\n\nenum SupportedPlatform {\n  android('Android'),\n  ios('iOS'),\n  linux('Linux'),\n  windows('Windows'),\n  macos('macOS'),\n  web('Web');\n\n  const SupportedPlatform(this.name);\n\n  final String name;\n}\n"
  },
  {
    "path": "examples/pub/lib/package_manager.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:isar/isar.dart';\nimport 'package:pub_app/asset_loader.dart';\nimport 'package:pub_app/models/asset.dart';\nimport 'package:pub_app/models/package.dart';\nimport 'package:pub_app/repository.dart';\n\nclass PackageManager {\n  const PackageManager(this.isar, this.repository);\n\n  final Isar isar;\n  final Repository repository;\n\n  Stream<Package> watchPackage(String name, {String? version}) async* {\n    final query = isar.packages\n        .where()\n        .nameEqualToAnyVersion(name)\n        .filter()\n        .optional(version == null, (q) => q.isLatestEqualTo(true))\n        .optional(version != null, (q) => q.versionEqualTo(version!))\n        .build();\n\n    await for (final results in query.watch(fireImmediately: true)) {\n      if (results.isNotEmpty) {\n        yield results.first;\n      }\n    }\n  }\n\n  Stream<List<Package>> watchPackageVersions(String name) async* {\n    final query = isar.packages\n        .where()\n        .nameEqualToAnyVersion(name)\n        .sortByPublishedDesc()\n        .build();\n\n    await for (final results in query.watch(fireImmediately: true)) {\n      if (results.isNotEmpty) {\n        yield results;\n      }\n    }\n  }\n\n  Stream<String> watchLatestVersion(String name) async* {\n    final query = isar.packages\n        .where()\n        .nameEqualToAnyVersion(name)\n        .filter()\n        .isLatestEqualTo(true)\n        .versionProperty()\n        .build();\n\n    await for (final results in query.watch(fireImmediately: true)) {\n      if (results.isNotEmpty) {\n        yield results.first;\n      }\n    }\n  }\n\n  Stream<String?> watchPreReleaseVersion(String name) async* {\n    await for (final _ in isar.packages.watchLazy(fireImmediately: true)) {\n      final latestDate = await isar.packages\n          .where()\n          .nameEqualToAnyVersion(name)\n          .filter()\n          .isLatestEqualTo(true)\n          .publishedProperty()\n          .findFirst();\n\n      if (latestDate != null) {\n        yield await isar.packages\n            .where()\n            .nameEqualToAnyVersion(name)\n            .filter()\n            .publishedGreaterThan(latestDate)\n            .sortByPublishedDesc()\n            .versionProperty()\n            .findFirst();\n      }\n    }\n  }\n\n  Future<void> loadPackage(\n    String name, {\n    bool loadMetrics = false,\n    String? version,\n  }) async {\n    final newPackageVersions = await repository.getPackageVersions(name);\n    final latestExistingDate = await isar.packages\n        .where()\n        .nameEqualToAnyVersion(name)\n        .publishedProperty()\n        .max();\n    final versionsToAdd = newPackageVersions\n        .where(\n          (e) =>\n              e.published.millisecondsSinceEpoch >\n              (latestExistingDate?.millisecondsSinceEpoch ?? 0),\n        )\n        .toList();\n\n    final currentLatest = await isar.packages\n        .where()\n        .nameEqualToAnyVersion(name)\n        .filter()\n        .isLatestEqualTo(true)\n        .findFirst();\n    final newLatestVersion =\n        newPackageVersions.firstWhere((e) => e.isLatest).version;\n    if (currentLatest != null && currentLatest.version != newLatestVersion) {\n      versionsToAdd.add(currentLatest.copyWith(isLatest: false));\n    }\n\n    if (loadMetrics) {\n      version ??= newLatestVersion;\n      final metrics = await repository.getPackageMetrics(name, version);\n      final package = newPackageVersions\n          .firstWhere((e) => e.version == version)\n          .copyWithMetrics(metrics);\n      versionsToAdd.add(package);\n    }\n    await isar.writeTxn(() async {\n      await isar.packages.putAll(versionsToAdd);\n    });\n  }\n\n  Stream<Map<AssetKind, String>> watchPackageAssets(\n    String name,\n    String version,\n  ) async* {\n    final query =\n        isar.assets.where().packageVersionEqualToAnyKind(name, version).build();\n\n    final existing = await query.findAll();\n    if (existing.isNotEmpty) {\n      yield {\n        for (final asset in existing) asset.kind: asset.content,\n      };\n    } else {\n      final existingAnyVersion = await isar.assets\n          .where()\n          .packageEqualToAnyVersionKind(name)\n          .sortByVersionDesc()\n          .findAll();\n      if (existingAnyVersion.isNotEmpty) {\n        final assets = <AssetKind, String>{};\n        for (final asset in existingAnyVersion) {\n          if (!assets.containsKey(asset.kind)) {\n            assets[asset.kind] = asset.content;\n          }\n        }\n        yield assets;\n      }\n    }\n\n    await for (final results in query.watch()) {\n      if (results.isNotEmpty) {\n        yield {\n          for (final asset in results) asset.kind: asset.content,\n        };\n      }\n    }\n  }\n\n  Future<void> loadPackageAssets(String name, String version) {\n    return compute(loadAssets, PackageAndVersion(name, version));\n  }\n\n  Future<List<String>> search(String query, int page, {bool online = true}) {\n    if (online) {\n      return repository.search(query, page + 1);\n    } else {\n      return isar.packages\n          .filter()\n          .nameContains(query, caseSensitive: false)\n          .or()\n          .descriptionContains(query, caseSensitive: false)\n          .sortByLikesDesc()\n          .distinctByName()\n          .offset(page * 10)\n          .limit(10)\n          .nameProperty()\n          .findAll();\n    }\n  }\n\n  Future<void> bulkLoad(String query) async {\n    var page = 0;\n    while (true) {\n      final packageNames = await search(query, page);\n      if (packageNames.isEmpty) {\n        break;\n      }\n      await Future.wait(\n        packageNames.map((e) => loadPackage(e, loadMetrics: true)),\n      );\n      page++;\n    }\n  }\n\n  Stream<List<String>> watchFavoriteNames() {\n    return isar.packages\n        .filter()\n        .flutterFavoriteEqualTo(true)\n        .distinctByName()\n        .nameProperty()\n        .watch(fireImmediately: true);\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/provider.dart",
    "content": "// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes\n\nimport 'dart:async';\n\nimport 'package:dio/dio.dart';\nimport 'package:isar/isar.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:pub_app/models/asset.dart';\nimport 'package:pub_app/models/package.dart';\nimport 'package:pub_app/package_manager.dart';\nimport 'package:pub_app/repository.dart';\nimport 'package:riverpod/riverpod.dart';\n\nfinal isarPod = FutureProvider((ref) async {\n  final dir = await getApplicationDocumentsDirectory();\n  return Isar.open([PackageSchema, AssetSchema], directory: dir.path);\n});\n\nfinal repositoryPod = Provider((ref) {\n  return Repository(Dio());\n});\n\nfinal packageManagerPod = FutureProvider((ref) async {\n  final isar = await ref.watch(isarPod.future);\n  final repository = ref.watch(repositoryPod);\n  return PackageManager(isar, repository);\n});\n\nfinal freshPackagePod =\n    StreamProvider.family<Package, PackageNameVersion>((ref, package) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  unawaited(\n    manager.loadPackage(\n      package.name,\n      version: package.version,\n      loadMetrics: true,\n    ),\n  );\n  yield* manager.watchPackage(package.name, version: package.version);\n});\n\nfinal packagePod =\n    StreamProvider.family<Package, PackageNameVersion>((ref, package) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  yield* manager.watchPackage(package.name, version: package.version);\n});\n\nfinal packageVersionsPod =\n    StreamProvider.family<List<Package>, String>((ref, package) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  yield* manager.watchPackageVersions(package);\n});\n\nfinal latestVersionPod =\n    StreamProvider.family<String, String>((ref, name) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  yield* manager.watchLatestVersion(name);\n});\n\nfinal preReleaseVersionPod =\n    StreamProvider.family<String?, String>((ref, name) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  yield* manager.watchPreReleaseVersion(name);\n});\n\nfinal assetsPod =\n    StreamProvider.family<Map<AssetKind, String>, PackageNameVersion>(\n        (ref, package) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  unawaited(manager.loadPackageAssets(package.name, package.version!));\n  yield* manager.watchPackageAssets(package.name, package.version!);\n});\n\nclass PackageNameVersion {\n  const PackageNameVersion(this.name, [this.version]);\n\n  final String name;\n  final String? version;\n\n  @override\n  int get hashCode => Object.hash(name, version);\n\n  @override\n  bool operator ==(Object other) =>\n      other is PackageNameVersion &&\n      name == other.name &&\n      version == other.version;\n}\n\nclass QueryPage {\n  const QueryPage(this.query, this.page);\n\n  final String query;\n  final int page;\n\n  @override\n  int get hashCode => Object.hash(query, page);\n\n  @override\n  bool operator ==(Object other) =>\n      other is QueryPage && query == other.query && page == other.page;\n}\n"
  },
  {
    "path": "examples/pub/lib/repository.dart",
    "content": "// ignore_for_file: avoid_dynamic_calls\n\nimport 'package:dio/dio.dart';\nimport 'package:pub_app/models/api/metrics.dart';\nimport 'package:pub_app/models/api/package.dart';\nimport 'package:pub_app/models/package.dart';\n\nconst _api = 'https://pub.dev/api';\n\nclass Repository {\n  Repository(this.dio);\n\n  final Dio dio;\n\n  Future<List<Package>> getPackageVersions(String name) async {\n    final response =\n        await dio.get<Map<String, dynamic>>('$_api/packages/$name');\n    final package = ApiPackage.fromJson(response.data!);\n    return Package.fromApiPackage(package);\n  }\n\n  Future<ApiPackageMetrics> getPackageMetrics(\n    String name,\n    String version,\n  ) async {\n    final response = await dio.get<Map<String, dynamic>>(\n      '$_api/packages/$name/versions/$version/score',\n    );\n    return ApiPackageMetrics.fromJson(response.data!);\n  }\n\n  Future<List<int>> downloadPackage(String name, String version) async {\n    final response = await dio.get<List<int>>(\n      '$_api/packages/$name/versions/$version/archive.tar.gz',\n      options: Options(responseType: ResponseType.bytes),\n    );\n    return response.data!;\n  }\n\n  Future<List<String>> search(String query, int page) async {\n    final response = await dio.get<Map<String, dynamic>>(\n      '$_api/search',\n      queryParameters: {\n        'q': query,\n        'page': page,\n      },\n    );\n    return (response.data!['packages'] as List)\n        .map((e) => e['package'] as String)\n        .toList();\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/app_bar.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:flutter_svg/flutter_svg.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:pub_app/main.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass PubAppBar extends ConsumerWidget implements PreferredSizeWidget {\n  const PubAppBar({super.key, this.favorite = false});\n\n  final bool favorite;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final darkMode = ref.watch(darkModePod);\n    return AppBar(\n      automaticallyImplyLeading: false,\n      title: GestureDetector(\n        onTap: () {\n          context.go('/');\n        },\n        child: AnimatedCrossFade(\n          firstChild: SvgPicture.asset('assets/pub_logo_dark.svg', width: 150),\n          secondChild: SvgPicture.asset('assets/pub_logo.svg', width: 150),\n          crossFadeState:\n              darkMode ? CrossFadeState.showFirst : CrossFadeState.showSecond,\n          duration: kThemeChangeDuration,\n        ),\n      ),\n      centerTitle: false,\n      actions: [\n        if (favorite)\n          Center(\n            child: ElevatedButton.icon(\n              onPressed: () {\n                launchUrl(\n                  Uri.parse(\n                    'https://docs.flutter.dev/development/packages-and-plugins/favorites',\n                  ),\n                );\n              },\n              icon: const FlutterLogo(),\n              label: const Text('Flutter Favorite'),\n            ),\n          ),\n        IconButton(\n          icon: Icon(\n            darkMode ? Icons.light_mode_rounded : Icons.dark_mode_rounded,\n          ),\n          onPressed: () {\n            ref.read(darkModePod.notifier).state = !darkMode;\n          },\n        ),\n      ],\n    );\n  }\n\n  @override\n  Size get preferredSize => AppBar().preferredSize;\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/detail_page.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:pub_app/models/asset.dart';\nimport 'package:pub_app/models/package.dart';\nimport 'package:pub_app/provider.dart';\nimport 'package:pub_app/ui/app_bar.dart';\nimport 'package:pub_app/ui/markdown_viewer.dart';\nimport 'package:pub_app/ui/package_metadata.dart';\nimport 'package:pub_app/ui/package_versions.dart';\n\nclass DetailPage extends ConsumerWidget {\n  const DetailPage({\n    super.key,\n    required this.name,\n    this.version,\n  });\n\n  final String name;\n  final String? version;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final package =\n        ref.watch(freshPackagePod(PackageNameVersion(name, version)));\n    return Scaffold(\n      appBar: PubAppBar(\n        favorite: package.valueOrNull?.flutterFavorite ?? false,\n      ),\n      body: SingleChildScrollView(\n        child: package.map(\n          data: (data) {\n            return Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.stretch,\n              children: [\n                Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 12),\n                  child: PackageHeader(package: data.value),\n                ),\n                const SizedBox(height: 20),\n                PackageBody(package: data.value),\n              ],\n            );\n          },\n          error: (err) {\n            return Center(child: Text('Error: $err'));\n          },\n          loading: (loading) => const Center(\n            child: CircularProgressIndicator(),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass PackageBody extends StatefulWidget {\n  const PackageBody({super.key, required this.package});\n\n  final Package package;\n\n  @override\n  State<PackageBody> createState() => _PackageBodyState();\n}\n\nclass _PackageBodyState extends State<PackageBody> {\n  _BodyPage page = _BodyPage.readme;\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      crossAxisAlignment: CrossAxisAlignment.stretch,\n      children: [\n        MediaQuery.removePadding(\n          removeBottom: true,\n          context: context,\n          child: NavigationBar(\n            height: 60,\n            labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,\n            selectedIndex: page.index,\n            destinations: const [\n              NavigationDestination(\n                icon: Icon(Icons.description_rounded),\n                label: 'Readme',\n              ),\n              NavigationDestination(\n                icon: Icon(Icons.change_history_rounded),\n                label: 'Changelog',\n              ),\n              /*NavigationDestination(\n                icon: Icon(Icons.adjust),\n                label: 'Example',\n              ),*/\n              NavigationDestination(\n                icon: Icon(Icons.list_alt_rounded),\n                label: 'Versions',\n              ),\n            ],\n            onDestinationSelected: (value) {\n              setState(() {\n                page = _BodyPage.values[value];\n              });\n            },\n          ),\n        ),\n        const SizedBox(height: 20),\n        if (page == _BodyPage.readme)\n          Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 12),\n            child: PackageAsset(\n              name: widget.package.name,\n              version: widget.package.version,\n              kind: AssetKind.readme,\n            ),\n          )\n        else if (page == _BodyPage.changelog)\n          Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 12),\n            child: PackageAsset(\n              name: widget.package.name,\n              version: widget.package.version,\n              kind: AssetKind.changelog,\n            ),\n          )\n        else if (page == _BodyPage.versions)\n          Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 12),\n            child: PackageVersions(name: widget.package.name),\n          ),\n      ],\n    );\n  }\n}\n\nenum _BodyPage {\n  readme,\n  changelog,\n  //example,\n  versions;\n}\n\nclass PackageAsset extends ConsumerWidget {\n  const PackageAsset({\n    super.key,\n    required this.name,\n    required this.version,\n    required this.kind,\n  });\n\n  final String name;\n  final String version;\n  final AssetKind kind;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final assets = ref.watch(assetsPod(PackageNameVersion(name, version)));\n    return assets.map(\n      data: (data) {\n        final md = data.value[kind];\n        if (md != null) {\n          return MarkdownViewer(markdown: md);\n        } else {\n          return const Center(\n            child: Text('This file is empty.'),\n          );\n        }\n      },\n      error: (err) => Center(child: Text('Error: $err')),\n      loading: (loading) => const Center(child: CircularProgressIndicator()),\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/home_page.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:pub_app/provider.dart';\nimport 'package:pub_app/ui/publisher.dart';\nimport 'package:pub_app/ui/search.dart';\n\nclass HomePage extends ConsumerStatefulWidget {\n  const HomePage({super.key});\n\n  @override\n  ConsumerState<HomePage> createState() => _HomePageState();\n}\n\nclass _HomePageState extends ConsumerState<HomePage> {\n  static bool refreshed = false;\n\n  @override\n  void initState() {\n    super.initState();\n\n    if (!refreshed) {\n      ref\n          .read(packageManagerPod.future)\n          .then((pm) => pm.bulkLoad('is:flutter-favorite'));\n      refreshed = true;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: SingleChildScrollView(\n        physics: const ClampingScrollPhysics(),\n        child: SafeArea(\n          top: false,\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.stretch,\n            children: [\n              SizedBox(\n                height: 230,\n                child: Search(\n                  onSearch: (query) {\n                    context.go('/search/$query');\n                  },\n                ),\n              ),\n              const Favorites(),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nfinal randomFavoriteNamesPod = StreamProvider((ref) async* {\n  final manager = await ref.watch(packageManagerPod.future);\n  Set<String>? previousNames;\n  await for (final packageNames in manager.watchFavoriteNames()) {\n    if (packageNames.isNotEmpty &&\n        (previousNames == null ||\n            !setEquals(packageNames.toSet(), previousNames))) {\n      previousNames = packageNames.toSet();\n      packageNames.shuffle();\n      yield packageNames.sublist(0, 10);\n    }\n  }\n});\n\nclass Favorites extends ConsumerWidget {\n  const Favorites({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final theme = Theme.of(context);\n    final favoriteNames = ref.watch(randomFavoriteNamesPod).valueOrNull;\n\n    return Padding(\n      padding: const EdgeInsets.only(left: 20, right: 20, top: 25),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        children: [\n          Text(\n            'Flutter Favorites',\n            style: theme.textTheme.headline3!.copyWith(\n              color: theme.colorScheme.primary,\n            ),\n          ),\n          Text(\n            'Some of the packages that demonstrate the highest levels '\n            'of quality, selected by the Flutter Ecosystem Committee',\n            style: theme.textTheme.subtitle1,\n          ),\n          if (favoriteNames != null) ...[\n            const SizedBox(height: 15),\n            for (final name in favoriteNames)\n              Padding(\n                padding: const EdgeInsets.symmetric(vertical: 6),\n                child: PackageCard(name: name),\n              ),\n          ],\n        ],\n      ),\n    );\n  }\n}\n\nclass PackageCard extends ConsumerWidget {\n  const PackageCard({super.key, required this.name});\n\n  final String name;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final theme = Theme.of(context);\n    final package = ref.watch(packagePod(PackageNameVersion(name))).valueOrNull;\n    return Card(\n      margin: EdgeInsets.zero,\n      clipBehavior: Clip.antiAlias,\n      child: Material(\n        color: Colors.transparent,\n        child: InkWell(\n          onTap: () {\n            context.push('/packages/$name');\n          },\n          child: Padding(\n            padding: const EdgeInsets.all(20),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Text(\n                  name,\n                  style: theme.textTheme.headline5!.copyWith(\n                    color: theme.colorScheme.onPrimaryContainer,\n                  ),\n                ),\n                if (package?.description != null) ...[\n                  const SizedBox(height: 5),\n                  Text(\n                    package!.description!.trim(),\n                    style: theme.textTheme.bodyText2,\n                  ),\n                ],\n                if (package?.publisher != null) ...[\n                  const SizedBox(height: 5),\n                  Publisher(package!.publisher!),\n                ],\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/markdown_viewer.dart",
    "content": "import 'package:clickup_fading_scroll/clickup_fading_scroll.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_html/flutter_html.dart';\nimport 'package:flutter_html_svg/flutter_html_svg.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:google_fonts/google_fonts.dart';\nimport 'package:markdown/markdown.dart' as md;\nimport 'package:url_launcher/url_launcher_string.dart';\n\nfinal _markdownHtmlPod = Provider.family<String, String>((ref, source) {\n  return md.markdownToHtml(\n    source,\n    extensionSet: md.ExtensionSet.gitHubWeb,\n  );\n});\n\nclass MarkdownViewer extends ConsumerWidget {\n  const MarkdownViewer({super.key, required this.markdown});\n\n  final String markdown;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final theme = Theme.of(context);\n    final html = ref.read(_markdownHtmlPod(markdown));\n    return Html(\n      data: html,\n      onLinkTap: (url, context, attributes, element) {\n        if (url != null) {\n          launchUrlString(url);\n        }\n      },\n      customRenders: {\n        svgTagMatcher(): svgTagRender(),\n        svgDataUriMatcher(): svgDataImageRender(),\n        svgAssetUriMatcher(): svgAssetImageRender(),\n        svgNetworkSourceMatcher(): svgNetworkImageRender(),\n        tagMatcher('code'): CustomRender.widget(\n          widget: (context, children) {\n            final code = context.tree.element!.text;\n            final codeBgColor =\n                theme.colorScheme.secondaryContainer.withOpacity(0.25);\n            if (code.contains('\\n')) {\n              return FadingScroll(\n                builder: (context, controller) {\n                  return SingleChildScrollView(\n                    scrollDirection: Axis.horizontal,\n                    controller: controller,\n                    child: Container(\n                      padding: const EdgeInsets.all(15),\n                      decoration: BoxDecoration(\n                        color: codeBgColor,\n                        borderRadius: BorderRadius.circular(8),\n                      ),\n                      child: SelectableText(\n                        code.trim(),\n                        style: GoogleFonts.jetBrainsMono(\n                          color: theme.colorScheme.onSecondaryContainer,\n                        ),\n                      ),\n                    ),\n                  );\n                },\n              );\n            } else {\n              return SelectableText(\n                code.trim(),\n                style: GoogleFonts.jetBrainsMono(\n                  backgroundColor: codeBgColor,\n                  color: theme.colorScheme.onSecondaryContainer,\n                ),\n              );\n            }\n          },\n        ),\n        tagMatcher('h1'): CustomRender.widget(\n          widget: (context, children) {\n            return Container(\n              width: double.infinity,\n              padding: const EdgeInsets.only(bottom: 5),\n              decoration: BoxDecoration(\n                border: Border(\n                  bottom: BorderSide(color: theme.dividerColor),\n                ),\n              ),\n              child: Text(\n                context.tree.element!.text,\n                style: context.tree.style.generateTextStyle(),\n              ),\n            );\n          },\n        ),\n        tagMatcher('h2'): CustomRender.widget(\n          widget: (context, children) {\n            return Container(\n              width: double.infinity,\n              padding: const EdgeInsets.only(bottom: 5),\n              decoration: BoxDecoration(\n                border: Border(\n                  bottom: BorderSide(color: theme.dividerColor),\n                ),\n              ),\n              child: Text(\n                context.tree.element!.text,\n                style: context.tree.style.generateTextStyle(),\n              ),\n            );\n          },\n        ),\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/package_metadata.dart",
    "content": "import 'package:clickup_fading_scroll/clickup_fading_scroll.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:pub_app/models/package.dart';\nimport 'package:pub_app/provider.dart';\nimport 'package:pub_app/ui/publisher.dart';\nimport 'package:timeago/timeago.dart' as timeago;\n\nclass PackageHeader extends ConsumerWidget {\n  const PackageHeader({super.key, required this.package});\n\n  final Package package;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final theme = Theme.of(context);\n\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      crossAxisAlignment: CrossAxisAlignment.stretch,\n      children: [\n        const SizedBox(height: 20),\n        Text(\n          '${package.name} ${package.version}',\n          style: theme.textTheme.headline5,\n        ),\n        const SizedBox(height: 3),\n        Wrap(\n          children: [\n            Text(\n              timeago.format(package.published),\n              style: theme.textTheme.subtitle2,\n            ),\n            if (package.publisher != null) ...[\n              Text(\n                ' • ',\n                style: theme.textTheme.subtitle2,\n              ),\n              Publisher(package.publisher!),\n            ],\n          ],\n        ),\n        const SizedBox(height: 15),\n        Scores(package: package),\n        const SizedBox(height: 15),\n        Platforms(package: package),\n        if (package.description != null) ...[\n          const SizedBox(height: 15),\n          Text(\n            package.description!.trim(),\n            style: theme.textTheme.bodyText2!.copyWith(\n              color: theme.colorScheme.onSurface.withOpacity(0.9),\n            ),\n          ),\n        ]\n      ],\n    );\n  }\n}\n\nclass Platforms extends StatelessWidget {\n  const Platforms({super.key, required this.package, this.compact = false});\n\n  final Package package;\n  final bool compact;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final platforms = package.platforms?.map((e) => e.name).toList()?..sort();\n    final sdks = [\n      if (package.dart == true) 'DART',\n      if (package.flutter == true) 'FLUTTER'\n    ];\n    return Wrap(\n      spacing: 5,\n      runSpacing: 5,\n      children: [\n        if (sdks.isNotEmpty)\n          Container(\n            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),\n            decoration: BoxDecoration(\n              color: theme.colorScheme.primaryContainer,\n              borderRadius: BorderRadius.circular(7),\n            ),\n            child: Text(\n              sdks.join(' • '),\n              style: theme.textTheme.subtitle2!.copyWith(\n                fontSize: compact ? 9 : 11,\n                color: theme.colorScheme.onPrimaryContainer,\n              ),\n            ),\n          ),\n        if (platforms?.isEmpty == false)\n          Container(\n            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),\n            decoration: BoxDecoration(\n              color: theme.colorScheme.primaryContainer,\n              borderRadius: BorderRadius.circular(7),\n            ),\n            child: Text(\n              platforms!.join(' • ').toUpperCase(),\n              style: theme.textTheme.subtitle2!.copyWith(\n                fontSize: compact ? 9 : 11,\n                color: theme.colorScheme.onPrimaryContainer,\n              ),\n            ),\n          ),\n      ],\n    );\n  }\n}\n\nclass Scores extends ConsumerWidget {\n  const Scores({\n    super.key,\n    required this.package,\n    this.alwaysShowLatest = false,\n  });\n\n  final Package package;\n  final bool alwaysShowLatest;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final latestVersion = ref.watch(latestVersionPod(package.name)).valueOrNull;\n    final preReleaseVersion =\n        ref.watch(preReleaseVersionPod(package.name)).valueOrNull;\n\n    final widgets = <Widget>[\n      if (package.likes != null)\n        ScoreItem(\n          stat: package.likes.toString(),\n          title: 'LIKES',\n        ),\n      if (package.points != null)\n        ScoreItem(\n          stat: package.points.toString(),\n          title: 'PUB POINTS',\n        ),\n      if (package.popularity != null)\n        ScoreItem(\n          stat: '${(package.popularity! * 100).round()}%',\n          title: 'POPULARITY',\n        ),\n      if (latestVersion != null &&\n          (latestVersion != package.version || alwaysShowLatest))\n        ScoreItem(\n          stat: latestVersion,\n          title: 'LATEST',\n          onTap: () {\n            context.push('/packages/${package.name}');\n          },\n        ),\n      if (preReleaseVersion != null)\n        ScoreItem(\n          stat: preReleaseVersion,\n          title: 'PRERELEASE',\n          onTap: () {\n            context\n                .push('/packages/${package.name}/versions/$preReleaseVersion');\n          },\n        ),\n    ];\n\n    return FadingScroll(\n      builder: (context, controller) {\n        return SingleChildScrollView(\n          scrollDirection: Axis.horizontal,\n          controller: controller,\n          child: IntrinsicHeight(\n            child: Row(\n              //crossAxisAlignment: CrossAxisAlignment.stretch,\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                for (var i = 0; i < widgets.length; i++) ...[\n                  if (i != 0) const VerticalDivider(thickness: 1, width: 0),\n                  widgets[i],\n                ]\n              ],\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass ScoreItem extends StatelessWidget {\n  const ScoreItem({\n    super.key,\n    required this.stat,\n    required this.title,\n    this.onTap,\n  });\n\n  final String stat;\n  final String title;\n  final VoidCallback? onTap;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return InkWell(\n      onTap: onTap,\n      child: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 8),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text(\n              stat,\n              style: theme.textTheme.titleMedium!.copyWith(\n                color: theme.colorScheme.primary,\n              ),\n            ),\n            Text(\n              title,\n              style: theme.textTheme.labelSmall!.copyWith(\n                fontSize: 9,\n                color: theme.colorScheme.onSurface.withOpacity(0.6),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/package_versions.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:pub_app/provider.dart';\nimport 'package:timeago/timeago.dart' as timeago;\n\nclass PackageVersions extends ConsumerWidget {\n  const PackageVersions({super.key, required this.name});\n\n  final String name;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final theme = Theme.of(context);\n    final versions = ref.watch(packageVersionsPod(name)).valueOrNull ?? [];\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        for (final version in versions) ...[\n          InkWell(\n            onTap: () {\n              context.push(\n                '/packages/$name/versions/${Uri.encodeComponent(version.version)}',\n              );\n            },\n            child: Padding(\n              padding: const EdgeInsets.symmetric(horizontal: 20),\n              child: Container(\n                padding: const EdgeInsets.symmetric(vertical: 5),\n                child: Row(\n                  children: [\n                    Text(\n                      version.version,\n                      style: theme.textTheme.headline5!.copyWith(\n                        color: theme.colorScheme.primary,\n                      ),\n                    ),\n                    const Spacer(),\n                    Text(timeago.format(version.published))\n                  ],\n                ),\n              ),\n            ),\n          ),\n          const Divider(),\n        ]\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/publisher.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass Publisher extends StatelessWidget {\n  const Publisher(this.publisher, {super.key});\n\n  final String publisher;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Row(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        const Icon(\n          Icons.verified_outlined,\n          size: 16,\n        ),\n        const SizedBox(width: 2),\n        Text(\n          publisher,\n          style: theme.textTheme.subtitle2!\n              .copyWith(color: theme.colorScheme.primary),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/search.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_svg/flutter_svg.dart';\nimport 'package:go_router/go_router.dart';\n\nclass Search extends StatefulWidget {\n  const Search({super.key, required this.onSearch, this.query});\n\n  final String? query;\n  final void Function(String query) onSearch;\n\n  @override\n  State<Search> createState() => _SearchState();\n}\n\nclass _SearchState extends State<Search> {\n  late final textController = TextEditingController(text: widget.query);\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: [\n        Positioned.fill(\n          child: SvgPicture.asset(\n            'assets/search_bg.svg',\n            fit: BoxFit.cover,\n          ),\n        ),\n        Positioned.fill(\n          child: SafeArea(\n            bottom: false,\n            child: Center(\n              child: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  GestureDetector(\n                    behavior: HitTestBehavior.opaque,\n                    onTap: () {\n                      context.go('/');\n                    },\n                    child: SvgPicture.asset(\n                      'assets/pub_logo_dark.svg',\n                      width: 250,\n                    ),\n                  ),\n                  const SizedBox(height: 20),\n                  Container(\n                    margin: const EdgeInsets.symmetric(\n                      vertical: 15,\n                      horizontal: 40,\n                    ),\n                    decoration: const BoxDecoration(\n                      color: Color(0xff35404d),\n                      borderRadius: BorderRadius.all(Radius.circular(50)),\n                    ),\n                    child: Padding(\n                      padding: const EdgeInsets.only(left: 10),\n                      child: Row(\n                        children: [\n                          const Icon(Icons.search, color: Colors.grey),\n                          const SizedBox(width: 10),\n                          Expanded(\n                            child: Center(\n                              child: TextField(\n                                controller: textController,\n                                style: const TextStyle(color: Colors.white),\n                                decoration: InputDecoration(\n                                  border: InputBorder.none,\n                                  hintText: 'Search packages',\n                                  hintStyle: TextStyle(\n                                    color: Colors.grey.shade400,\n                                  ),\n                                ),\n                                onSubmitted: (query) {\n                                  if (query.isNotEmpty) {\n                                    widget.onSearch(query);\n                                  }\n                                },\n                                onEditingComplete: () {\n                                  FocusManager.instance.primaryFocus?.unfocus();\n                                },\n                              ),\n                            ),\n                          ),\n                        ],\n                      ),\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          ),\n        )\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/lib/ui/search_page.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:pub_app/provider.dart';\nimport 'package:pub_app/ui/package_metadata.dart';\nimport 'package:pub_app/ui/search.dart';\n\nclass SearchPage extends ConsumerStatefulWidget {\n  const SearchPage({super.key, required this.query});\n\n  final String query;\n\n  @override\n  ConsumerState<SearchPage> createState() => _SearchPageState();\n}\n\nclass _SearchPageState extends ConsumerState<SearchPage> {\n  final controller = ScrollController();\n  final List<String> packages = [];\n\n  late String query = widget.query;\n  bool loading = false;\n  bool online = true;\n\n  @override\n  void initState() {\n    super.initState();\n    controller.addListener(() {\n      if (controller.position.extentAfter < 500 && !loading) {\n        _loadMore();\n      }\n    });\n    _loadMore();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: Column(\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        children: [\n          SizedBox(\n            height: 230,\n            child: Search(\n              query: widget.query,\n              onSearch: (q) {\n                setState(() {\n                  query = q;\n                  packages.clear();\n                });\n                _loadMore();\n              },\n            ),\n          ),\n          Padding(\n            padding: const EdgeInsets.symmetric(vertical: 10),\n            child: Center(\n              child: ChoiceChip(\n                label: Text(online ? 'Online' : 'Offline'),\n                selected: online,\n                onSelected: (value) {\n                  setState(() {\n                    online = value;\n                    packages.clear();\n                  });\n                  _loadMore();\n                },\n              ),\n            ),\n          ),\n          if (packages.isNotEmpty)\n            Expanded(\n              child: ListView.builder(\n                controller: controller,\n                itemBuilder: (context, index) {\n                  return SearchResult(name: packages[index]);\n                },\n                itemCount: packages.length,\n              ),\n            )\n          else if (loading)\n            const Expanded(\n              child: Center(child: CircularProgressIndicator()),\n            )\n          else\n            const Expanded(child: Center(child: Text('No Results'))),\n        ],\n      ),\n    );\n  }\n\n  Future<void> _loadMore() async {\n    try {\n      loading = true;\n      final manager = await ref.read(packageManagerPod.future);\n      final newPackages =\n          await manager.search(query, packages.length ~/ 10, online: online);\n      if (mounted) {\n        setState(() {\n          packages.addAll(newPackages);\n        });\n      }\n    } finally {\n      loading = false;\n    }\n  }\n}\n\nclass SearchResult extends ConsumerWidget {\n  const SearchResult({super.key, required this.name});\n\n  final String name;\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final theme = Theme.of(context);\n    final package =\n        ref.watch(freshPackagePod(PackageNameVersion(name))).valueOrNull;\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.stretch,\n      children: [\n        InkWell(\n          onTap: () {\n            context.push('/packages/$name');\n          },\n          child: Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Text(name, style: theme.textTheme.headline6),\n                if (package?.description != null) ...[\n                  const SizedBox(height: 4),\n                  Text(\n                    package!.description!,\n                    style: theme.textTheme.bodyText2!.copyWith(fontSize: 13),\n                    maxLines: 4,\n                  ),\n                ],\n                if (package != null) ...[\n                  const SizedBox(height: 12),\n                  Scores(\n                    package: package,\n                    alwaysShowLatest: true,\n                  ),\n                ],\n                if (package?.platforms?.isEmpty == false ||\n                    package?.flutter == true ||\n                    package?.dart == true) ...[\n                  const SizedBox(height: 12),\n                  Platforms(package: package!, compact: true),\n                ]\n              ],\n            ),\n          ),\n        ),\n        const Divider(),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "examples/pub/pubspec.yaml",
    "content": "name: pub_app\ndescription: A new Flutter project.\npublish_to: \"none\"\nversion: 1.0.0+1\n\nenvironment:\n  sdk: \">=2.17.0 <3.0.0\"\n  flutter: \">=1.17.0\"\n\nisar_version: &isar_version 3.0.2 # define the version to be used\n\ndependencies:\n  auto_size_text: ^3.0.0\n  clickup_fading_scroll:\n    git:\n      url: https://github.com/clickup/clickup_fading_scroll.git\n  copy_with_extension: ^4.0.3\n  dio: ^4.0.0\n  flutter:\n    sdk: flutter\n  flutter_riverpod: ^2.0.0\n  google_fonts: ^3.0.1\n  isar: *isar_version\n  isar_flutter_libs: *isar_version\n  json_annotation: ^4.6.0\n  markdown: ^6.0.0\n  pub_semver: ^2.1.1\n  pubspec: ^2.3.0\n  pubspec_parse: ^1.2.1\n  riverpod: ^2.0.0\n  shimmer: ^2.0.0\n  tar: ^0.5.6\n  timeago: ^3.3.0\n  url_launcher: ^6.1.5\n  go_router: ^5.0.0\n  flutter_svg: ^1.1.5\n  flutter_html: ^3.0.0-alpha.6\n  flutter_html_svg: ^3.0.0-alpha.4\n\ndev_dependencies:\n  build_runner: ^2.0.0\n  copy_with_extension_gen: ^4.0.3\n  isar_generator: *isar_version\n  json_serializable: ^6.3.0\n  flutter_lints: ^2.0.1\n\nflutter:\n  uses-material-design: true\n  assets:\n    - assets/ff_banner.png\n    - assets/pub_logo.svg\n    - assets/pub_logo_dark.svg\n    - assets/search_bg.svg\n"
  },
  {
    "path": "packages/isar/.gitignore",
    "content": "*.g.dart"
  },
  {
    "path": "packages/isar/CHANGELOG.md",
    "content": "## 3.1.8\n\n### Fixes\n\nFix Android release build on Flutter 3.24.0\n\n## 3.1.7\n\n### Fixes\n\nAdd apple privacy manifest\n\n## 3.1.6\n\n### Breaking\n\nSorry, but we had to shift package hosting to https://pub.isar-community.dev\n\n## 3.1.4\n\n- Fix inspector URL\n\n## 3.1.3\n\n- Maintenance release, mainly adapts build dependencies\n\n## 3.1.0+1\n\n### Fixes\n\n- Fixed error building MacOS library\n\n## 3.1.0\n\n### Breaking\n\nSorry for this breaking change. Unfortunately, it was necessary to fix stability issues on Android.\n\n- `directory` is now required for `Isar.open()` and `Isar.openSync()`\n\n### Fixes\n\n- Fixed a crash that occasionally occurred when opening Isar\n- Fixed a schema migration issue\n- Fixed an issue where embedded class renaming didn't work correctly\n\n### Enhancements\n\n- Many internal improvements\n- Performance improvements\n\n## 3.0.6\n\n### Fixes\n\n- Add check to verify transactions are used for correct instance\n- Add check to verify that async transactions are still active\n- Fix upstream issue with opening databases\n\n## 3.0.5\n\n### Enhancements\n\n- Improved performance for all operations\n- Added `maxSizeMiB` option to `Isar.open()` to specify the maximum size of the database file\n- Significantly reduced native library size\n- With the help of the community, the docs have been translated into a range of languages\n- Improved API docs\n- Added integration tests for more platforms to ensure high-quality releases\n- Support for unicode paths on Windows\n\n### Fixes\n\n- Fixed crash while opening Isar\n- Fixed crash on older Android devices\n- Fixed a native port that was not closed correctly in some cases\n- Added swift version to podspec\n- Fixed crash on Windows\n- Fixed \"IndexNotFound\" error\n\n## 3.0.4\n\nREDACTED.\n\n## 3.0.3\n\nREDACTED.\n\n## 3.0.2\n\n### Enhancements\n\n- The Inspector now supports creating objects and importing JSON\n- Added Inspector check to make sure Chrome is used\n\n### Fixes\n\n- Added support for the latest analyzer\n- Fixed native ports that were not closed correctly in some cases\n- Added support for Ubuntu 18.04 and older\n- Fixed issue with aborting transactions\n- Fixed crash when invalid JSON was provided to `importJsonRaw()`\n- Added missing `exportJsonSync()` and `exportJsonRawSync()`\n- Fixed issue where secondary instance could not be selected in the Inspector\n\n## 3.0.1\n\n### Enhancements\n\n- Support for arm64 iOS Simulators\n\n### Fixes\n\n- Fixed issue where `.anyOf()`, `.allOf()`, and `.oneOf()` could not be negated\n- Fixed too low min-iOS version. The minimum supported is 11.0\n- Fixed error during macOS App Store build\n\n## 3.0.0\n\nThis release has been a lot of work! Thanks to everyone who contributed and joined the countless discussions. You are really awesome!\n\nSpecial thanks to [@Jtplouffe](https://github.com/Jtplouffe) and [@Peyman](https://github.com/Viper-Bit) for their incredible work.\n\n### Web support\n\nThis version does not support the web target yet. It will be back in the next version. Please continue using 2.5.0 if you need web support.\n\n### Enhancements\n\n- Completely new Isar inspector that does not need to be installed anymore\n- Extreme performance improvements for almost all operations (up to 50%)\n- Support for embedded objects using `@embedded`\n- Support for enums using `@enumerated`\n- Vastly improved Isar binary format space efficiency resulting in about 20% smaller databases\n- Added `id`, `byte`, `short` and `float` typedefs\n- `IsarLinks` now support all `Set` methods based on the Isar `Id` of objects\n- Added `download` option to `Isar.initializeIsarCore()` to download binaries automatically\n- Added `replace` option for indexes\n- Added verification for correct Isar binary version\n- Added `collection.getSize()` and `collection.getSizeSync()`\n- Added `query.anyOf()` and `query.allOf()` query modifiers\n- Support for much more complex composite index queries\n- Support for logical XOR and the `.oneOf()` query modifier\n- Made providing a path optional\n- The default Isar name is now `default` and stored in `dir/name.isar` and `dir/name.isar.lock`\n- On non-web platforms, `IsarLink` and `IsarLinks` will load automatically\n- `.putSync()`, `.putAllSync()` etc. will now save links recursively by default\n- Added `isar.getSize()` and `isar.getSizeSync()`\n- Added `linksLengthEqualTo()`, `linksIsEmpty()`, `linksIsNotEmpty()`, `linksLengthGreaterThan()`, `linksLengthLessThan()`, `linksLengthBetween()` and `linkIsNull()` filters\n- Added `listLengthEqualTo()`, `listIsEmpty()`, `listIsNotEmpty()`, `listLengthGreaterThan()`, `listLengthLessThan()`, `listLengthBetween()` filters\n- Added `isNotNull()` filters\n- Added `compactOnLaunch` conditions to `Isar.open()` for automatic database compaction\n- Added `isar.copyToFile()` which copies a compacted version of the database to a path\n- Added check to verify that linked collections schemas are provided for opening an instance\n- Apply default values from constructor during deserialization\n- Added `isar.verify()` and `col.verify()` methods for checking database integrity in unit tests\n- Added missing float and double queries and an `epsilon` parameter\n\n### Breaking changes\n\n- Removed `TypeConverter` support in favor of `@embedded` and `@enumerated`\n- Removed `@Id()` and `@Size32()` annotations in favor of the `Id` and `short` types\n- Changed the `schemas` parameter from named to positional\n- The maximum size of objects is now 16MB\n- Removed `replaceOnConflict` and `saveLinks` parameter from `collection.put()` and `collection.putAll()`\n- Removed `isar` parameter from `Isar.txn()`, `Isar.writeTxn()`, `Isar.txnSync()` and `Isar.writeTxnSync()`\n- Removed `query.repeat()`\n- Removed `query.sortById()` and `query.distinctById()`\n- Fixed `.or()` instead of `.and()` being used implicitly when combining filters\n- Renamed multi-entry where clauses from `.yourListAnyEqualTo()` to `.yourListElementEqualTo()` to avoid confusion\n- Isar will no longer create the provided directory. Make sure it exists before opening an Isar Instance.\n- Changed the default index type for all `List`s to `IndexType.hash`\n- Renamed `isar.getCollection()` to `isar.collection()`\n- It is no longer allowed to extend or implement another collection\n- Unsupported properties will no longer be ignored by default\n- Renamed the `initialReturn` parameter to `fireImmediately`\n- Renamed `Isar.initializeLibraries()` to `Isar.initializeIsarCore()`\n\n### Fixes\n\nThere are too many fixes to list them all.\n\n- A lot of link fixes and a slight behavior change to make them super reliable\n- Fixed missing symbols on older Android phones\n- Fixed composite queries\n- Fixed various generator issues\n- Fixed error retrieving the id property in a query\n- Fixed missing symbols on 32-bit Android 5 & 6 devices\n- Fixed inconsistent `null` handling in json export\n- Fixed default directory issue on Android\n- Fixed different where clauses returning duplicate results\n- Fixed hash index issue where multiple list values resulted in the same hash\n- Fixed edge case where creating a new index failed\n\n## 2.5.0\n\n### Enhancements\n\n- Support for Android x86 (32 bit emulator) and macOS arm64 (Apple Silicon)\n- Greatly improved test coverage for sync methods\n- `col.clear()` now resets the auto increment counter to `0`\n- Significantly reduced Isar Core binary size (about 1.4MB -> 800KB)\n\n### Minor Breaking\n\n- Changed `initializeLibraries(Map<String, String> libraries)` to `initializeLibraries(Map<IsarAbi, String> libraries)`\n- Changed min Dart SDK to `2.16.0`\n\n### Fixes\n\n- Fixed issue with `IsarLink.saveSync()`\n- Fixed `id` queries\n- Fixed error thrown by `BroadcastChannel` in Firefox\n- Fixed Isar Inspector connection issue\n\n## 2.4.0\n\n### Enhancements\n\n- Support for querying links\n- Support for filtering and sorting links\n- Added methods to update and count links without loading them\n- Added `isLoaded` property to links\n- Added methods to count the number of objects in a collection\n- Big internal improvements\n\n### Minor Breaking\n\n- There are now different kinds of where clauses for dynamic queries\n- `isar.getCollection()` no longer requires the name of the collection\n- `Isar.instanceNames` now returns a `Set` instead of a `List`\n\n### Fixes\n\n- Fixed iOS crash that frequently happened on older devices\n- Fixed 32bit issue on Android\n- Fixed link issues\n- Fixed missing `BroadcastChannel` API for older Safari versions\n\n## 2.2.1\n\n### Enhancements\n\n- Reduced Isar web code size by 50%\n- Made `directory` parameter of `Isar.open()` optional for web\n- Made `name` parameter of `Isar.getInstance()` optional\n- Added `Isar.defaultName` constant\n- Enabled `TypeConverter`s with supertypes\n- Added message if `TypeConverter` nullability doesn't match\n- Added more tests\n\n### Fixes\n\n- Fixed issue with date queries\n- Fixed `FilterGroup.not` constructor (thanks for the PR @jtzell)\n\n## 2.2.0\n\nIsar now has full web support 🎉. No changes to your code required, just run it.\n\n_Web passes all unit tests but is still considered beta for now._\n\n### Minor Breaking\n\n- Added `saveLinks` parameter to `.put()` and `.putAll()` which defaults to `false`\n- Changed default `overrideChanges` parameter of `links.load()` to `true` to avoid unintended behavior\n\n### Enhancements\n\n- Full web support!\n- Improved write performance\n- Added `deleteFromDisk` option to `isar.close()`\n- Added `.reset()` and `.resetSync()` methods to `IsarLink` and `IsarLinks`\n- Improved `links.save()` performance\n- Added many tests\n\n### Fixed\n\n- Fixed value of `null` dates to be `DateTime.fromMillisecondsSinceEpoch(0)`\n- Fixed problem with migration\n- Fixed incorrect list values for new properties (`[]` instead of `null`)\n- Improved handling of link edge-cases\n\n## 2.1.4\n\n- Removed `path` dependency\n- Fixed incorrect return value of `deleteByIndex()`\n- Fixed wrong auto increment ids in some cases (thanks @robban112)\n- Fixed an issue with `Isar.close()` (thanks @msxenon)\n- Fixed `$` escaping in generated code (thanks @jtzell)\n- Fixed broken link in pub.dev example page\n\n## 2.1.0\n\n`isar_connect` is now integrated into `isar`\n\n### Enhancements\n\n- Added check for outdated generated files\n- Added check for changed schema across isolates\n- Added `Isar.openSync()`\n- Added `col.importJsonRawSync()`, `col.importJsonSync()`, `query.exportJsonRawSync()`, `query.exportJsonSync()`\n- Improved performance for queries\n- Improved handling of ffi memory\n- More tests\n\n### Fixed\n\n- Fixed issue where imported json required existing ids\n- Fixed issue with transaction handling (thanks @Peng-Qian for the awesome help)\n- Fixed issue with `@Ignore` annotation not always working\n- Fixed issue with `getByIndex()` not returning correct object id (thanks @jtzell)\n\n## 2.0.0\n\n### Breaking\n\n- The id for non-final objects is now assigned automatically after `.put()` and `.putSync()`\n- `double` and `List<double>` indexes can no longer be at the beginning of a composite index\n- `List<double>` indexes can no longer be hashed\n- `.greaterThan()`, `.lessThan()` and `.between()` filters and are now excluding for `double` values (`>=` -> `>`)\n- Changed the default index type for lists to `IndexType.value`\n- `IsarLink` and `IsarLinks` will no longer be initialized by Isar and must not be `nullable` or `late`.\n- Dart `2.14` or higher is required\n\n### Enhancements\n\n- Added API docs for all public methods\n- Added `isar.clear()`, `isar.clearSync()`, `col.clear()` and `col.clearSync()`\n- Added `col.filter()` as shortcut for `col.where().filter()`\n- Added `include` parameter to `.greaterThan()` and `.lessThan()` filters and where clauses\n- Added `includeLower` and `includeUpper` parameters to `.between()` filters and where clauses\n- Added `Isar.autoIncrement` to allow non-nullable auto-incrementing ids\n- `Isar.close()` now returns whether the last instance was closed\n- List values in composite indexes are now of type `IndexType.hash` automatically\n- Allowed multiple indexes on the same property\n- Removed exported packages from API docs\n- Improved generated code\n- Improved Isar Core error messages\n- Minor performance improvements\n- Automatic XCode configuration\n- Updated analyzer to `3.0.0`\n- More tests\n\n### Fixed\n\n- `IsarLink` and `IsarLinks` can now be final\n- Fixed multi-entry index queries returning items multiple times in some cases\n- Fixed `.anyLessThan()` and `.anyGreaterThan()` issues\n- Fixed issues with backlinks\n- Fixed issue where query only returned the first `99999` results\n- Fixed issue with id where clauses\n- Fixed default index type for lists and bytes\n- Fixed issue where renaming indexes was not possible\n- Fixed issue where wrong index name was used for `.getByX()` and `.deleteByX()`\n- Fixed issue where composite indexes did not allow non-hashed Strings as last value\n- Fixed issue where `@Ignore()` fields were not ignored\n\n## 1.0.5\n\n### Enhancements\n\n- Updated dependencies\n\n### Fixes:\n\n- Included desktop binaries\n- Fixed \"Cannot allocate memory\" error on older iOS devices\n- Fixed stripped binaries for iOS release builds\n- Fixed IsarInspector issues (thanks to [RubenBez](https://github.com/RubenBez) and [rizzi37](https://github.com/rizzi37))\n\n## 1.0.0+1\n\nAdded missing binaries\n\n## 1.0.0\n\nSwitched from liblmdb to libmdbx for better performance, more stability and many internal improvements.\n\n### Breaking\n\nThe internal database format has been changed to improve performance. Old databases do not work anymore!\n\n### Fixes\n\n- Fix issue with links being removed after object update\n- Fix String index problems\n\n### Enhancements\n\n- Support `greaterThan`, `lessThan` and `between` queries for String values\n- Support for inheritance (enabled by default)\n- Support for `final` properties and getters\n- Support for `freezed` and other code generators\n- Support getting / deleting objects by a key `col.deleteByName('Anne')`\n- Support for list indexes (hash an element based)\n- Generator now creates individual files instead of one big file\n- Allow specifying the collection accessor name\n- Unsupported properties are now ignored automatically\n- Returns the assigned ids after `.put()` operations (objects are no longer mutated)\n- Introduces `replaceOnConflict` option for `.put()` (instead of specifying it for index)\n- many more...\n\n### Internal\n\n- Improve generated code\n- Many new unit tests\n\n## 0.4.0\n\n### Breaking\n\n- Remove `.where...In()` and `...In()` extension methods\n- Split `.watch(lazy: bool)` into `.watch()` and `.watchLazy()`\n- Remove `include` option for filters\n\n### Fixes\n\n- Generate id for JSON imports that don't have an id\n- Enable `sortBy` and `thenBy` generation\n\n### Enhancements\n\n- Add `.optional()` and `.repeat()` query modifiers\n- Support property queries\n- Support query aggregation\n- Support dynamic queries (for custom query languages)\n- Support multi package configuration with `@ExternalCollection()`\n- Add `caseSensitive` option to `.distinctBy()`\n\n### Internal\n\n- Change iOS linking\n- Improve generated code\n- Set up integration tests and improve unit tests\n- Use CORE/0.4.0\n\n## 0.2.0\n\n- Link support\n- Many improvements and fixes\n\n## 0.1.0\n\n- Support for links and backlinks\n\n## 0.0.4\n\n- Bugfixes and many improvements\n\n## 0.0.2\n\nFix dependency issue\n\n## 0.0.1\n\nInitial release\n"
  },
  {
    "path": "packages/isar/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2022 Simon Leier\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "packages/isar/README.md",
    "content": "> 🚨 Please use the [renamed repository](https://github.com/isar-community/isar-community) (`isar-community/isar-community`) 🚨  \n>\n> This repository is no longer maintained and will not receive any further updates or fixes.  \n> \n> The new repository will continue active development and is also published on [pub.dev/packages/isar_community](https://pub.dev/packages/isar_community) under the new package name.  \n>\n> We strongly recommend migrating to the new repository for the latest improvements, bug fixes, and community support.\n\n\n\n\n> ⚠️ This repository is a fork of the [original project](https://github.com/isar/isar), focusing primarily on bug fixes and small updates for version 3. Our objective is to enhance the stability and reliability of the codebase while implementing minor improvements to refine the user experience. See details below on how to use this community fork.\n\n<p align=\"center\">\n  <a href=\"https://isar.dev\">\n    <img src=\"https://raw.githubusercontent.com/isar/isar/main/.github/assets/isar.svg?sanitize=true\" height=\"128\">\n  </a>\n  <h1 align=\"center\">Isar Database</h1>\n</p>\n\n<p align=\"center\">\n  \n  <img src=\"https://img.shields.io/badge/status-archived-red.svg\">\n  <a href=\"https://pub.dev/packages/isar\">\n    <img src=\"https://img.shields.io/pub/v/isar?label=pub.dev&labelColor=333940&logo=dart\">\n  </a>\n  <a href=\"https://github.com/isar-community/isar/actions/workflows/test.yaml\">\n    <img src=\"https://img.shields.io/github/actions/workflow/status/isar/isar/test.yaml?branch=main&label=tests&labelColor=333940&logo=github\">\n  </a>\n  <a href=\"https://app.codecov.io/gh/isar/isar\">\n    <img src=\"https://img.shields.io/codecov/c/github/isar/isar?logo=codecov&logoColor=fff&labelColor=333940\">\n  </a>\n  <a href=\"https://t.me/isardb\">\n    <img src=\"https://img.shields.io/static/v1?label=join&message=isardb&labelColor=333940&logo=telegram&logoColor=white&color=229ED9\">\n  </a>\n  <a href=\"https://twitter.com/simonleier\">\n    <img src=\"https://img.shields.io/twitter/follow/simcdev?style=social\">\n  </a>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://isar.dev\">Quickstart</a> •\n  <a href=\"https://isar.dev/schema\">Documentation</a> •\n  <a href=\"https://github.com/isar-community/isar/tree/main/examples/\">Sample Apps</a> •\n  <a href=\"https://github.com/isar-community/isar/discussions\">Support & Ideas</a> •\n  <a href=\"https://pub.dev/packages/isar\">Pub.dev</a>\n</p>\n\n> #### Isar [ee-zahr]:\n>\n> 1. River in Bavaria, Germany.\n> 2. [Crazy fast](#benchmarks) NoSQL database that is a joy to use.\n\n## Features\n\n- 💙 **Made for Flutter**. Easy to use, no config, no boilerplate\n- 🚀 **Highly scalable** The sky is the limit (pun intended)\n- 🍭 **Feature rich**. Composite & multi-entry indexes, query modifiers, JSON support etc.\n- ⏱ **Asynchronous**. Parallel query operations & multi-isolate support by default\n- 🦄 **Open source**. Everything is open source and free forever!\n\nIsar database can do much more (and we are just getting started)\n\n- 🕵️ **Full-text search**. Make searching fast and fun\n- 📱 **Multiplatform**. iOS, Android, Desktop\n- 🧪 **ACID semantics**. Rely on database consistency\n- 💃 **Static typing**. Compile-time checked and autocompleted queries\n- ✨ **Beautiful documentation**. Readable, easy to understand and ever-improving\n\nJoin the [Telegram group](https://t.me/isardb) for discussion and sneak peeks of new versions of the DB.\n\nIf you want to say thank you, star us on GitHub and like us on pub.dev 🙌💙\n\n## Quickstart\n\nHoly smokes you're here! Let's get started on using the coolest Flutter database out there...\n\n### 1. Add to pubspec.yaml\n\n```yaml\nisar_version: &isar_version 3.1.8 # define the version to be used\n\ndependencies:\n  isar: \n    version: *isar_version\n    hosted: https://pub.isar-community.dev/\n  isar_flutter_libs: # contains Isar Core\n    version: *isar_version\n    hosted: https://pub.isar-community.dev/\n\ndev_dependencies:\n  isar_generator: \n    version: *isar_version\n    hosted: https://pub.isar-community.dev/\n  build_runner: any\n\n```\n\n### 2. Annotate a Collection\n\n```dart\npart 'email.g.dart';\n\n@collection\nclass Email {\n  Id id = Isar.autoIncrement; // you can also use id = null to auto increment\n\n  @Index(type: IndexType.value)\n  String? title;\n\n  List<Recipient>? recipients;\n\n  @enumerated\n  Status status = Status.pending;\n}\n\n@embedded\nclass Recipient {\n  String? name;\n\n  String? address;\n}\n\nenum Status {\n  draft,\n  pending,\n  sent,\n}\n```\n\n### 3. Open a database instance\n\n```dart\nfinal dir = await getApplicationDocumentsDirectory();\nfinal isar = await Isar.open(\n  [EmailSchema],\n  directory: dir.path,\n);\n```\n\n### 4. Query the database\n\n```dart\nfinal emails = await isar.emails.filter()\n  .titleContains('awesome', caseSensitive: false)\n  .sortByStatusDesc()\n  .limit(10)\n  .findAll();\n```\n\n## Isar Database Inspector\n\nThe Isar Inspector allows you to inspect the Isar instances & collections of your app in real-time. You can execute queries, edit properties, switch between instances and sort the data.\n\n<img src=\"https://raw.githubusercontent.com/isar/isar/main/.github/assets/inspector.gif\">\n\nTo launch the inspector, just run your Isar app in debug mode and open the Inspector link in the logs.\n\n## CRUD operations\n\nAll basic crud operations are available via the `IsarCollection`.\n\n```dart\nfinal newEmail = Email()..title = 'Amazing new database';\n\nawait isar.writeTxn(() {\n  await isar.emails.put(newEmail); // insert & update\n});\n\nfinal existingEmail = await isar.emails.get(newEmail.id!); // get\n\nawait isar.writeTxn(() {\n  await isar.emails.delete(existingEmail.id!); // delete\n});\n```\n\n## Database Queries\n\nIsar database has a powerful query language that allows you to make use of your indexes, filter distinct objects, use complex `and()`, `or()` and `.xor()` groups, query links and sort the results.\n\n```dart\nfinal importantEmails = isar.emails\n  .where()\n  .titleStartsWith('Important') // use index\n  .limit(10)\n  .findAll()\n\nfinal specificEmails = isar.emails\n  .filter()\n  .recipient((q) => q.nameEqualTo('David')) // query embedded objects\n  .or()\n  .titleMatches('*university*', caseSensitive: false) // title containing 'university' (case insensitive)\n  .findAll()\n```\n\n## Database Watchers\n\nWith Isar database, you can watch collections, objects, or queries. A watcher is notified after a transaction commits successfully and the target actually changes.\nWatchers can be lazy and not reload the data or they can be non-lazy and fetch new results in the background.\n\n```dart\nStream<void> collectionStream = isar.emails.watchLazy();\n\nStream<List<Post>> queryStream = importantEmails.watch();\n\nqueryStream.listen((newResult) {\n  // do UI updates\n})\n```\n\n## Benchmarks\n\nBenchmarks only give a rough idea of the performance of a database but as you can see, Isar NoSQL database is quite fast 😇\n\n| <img src=\"https://raw.githubusercontent.com/isar-community/isar/v3/.github/assets/benchmarks/insert.png\" width=\"100%\" /> | <img src=\"https://raw.githubusercontent.com/isar-community/isar/v3/.github/assets/benchmarks/query.png\" width=\"100%\" /> |\n| ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |\n| <img src=\"https://raw.githubusercontent.com/isar-community/isar/v3/.github/assets/benchmarks/delete.png\" width=\"100%\" /> | <img src=\"https://raw.githubusercontent.com/isar-community/isar/v3/.github/assets/benchmarks/size.png\" width=\"100%\" />  |\n\nIf you are interested in more benchmarks or want to check how Isar performs on your device you can run the [benchmarks](https://github.com/isar-community/isar_benchmark) yourself.\n\n## Unit tests\n\nIf you want to use Isar database in unit tests or Dart code, call `await Isar.initializeIsarCore(download: true)` before using Isar in your tests.\n\nIsar NoSQL database will automatically download the correct binary for your platform. You can also pass a `libraries` map to adjust the download location for each platform.\n\nMake sure to use `flutter test -j 1` to avoid tests running in parallel. This would break the automatic download.\n\n## Contributors ✨\n\nBig thanks go to these wonderful people:\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tbody>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/AlexisL61\"><img src=\"https://avatars.githubusercontent.com/u/30233189?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Alexis</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/buraktabn\"><img src=\"https://avatars.githubusercontent.com/u/49204989?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Burak</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/CarloDotLog\"><img src=\"https://avatars.githubusercontent.com/u/13763473?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Carlo Loguercio</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Frostedfox\"><img src=\"https://avatars.githubusercontent.com/u/84601232?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Frostedfox</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/hafeezrana\"><img src=\"https://avatars.githubusercontent.com/u/87476445?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Hafeez Rana</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/h1376h\"><img src=\"https://avatars.githubusercontent.com/u/3498335?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Hamed H.</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Jtplouffe\"><img src=\"https://avatars.githubusercontent.com/u/32107801?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>JT</b></sub></a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/ritksm\"><img src=\"https://avatars.githubusercontent.com/u/111809?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Jack Rivers</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/nohli\"><img src=\"https://avatars.githubusercontent.com/u/43643339?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Joachim Nohl</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/vothvovo\"><img src=\"https://avatars.githubusercontent.com/u/20894472?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Johnson</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/VoidxHoshi\"><img src=\"https://avatars.githubusercontent.com/u/55886143?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>LaLucid</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/letyletylety\"><img src=\"https://avatars.githubusercontent.com/u/16468579?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Lety</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/lodisy\"><img src=\"https://avatars.githubusercontent.com/u/8101584?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Michael</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Moseco\"><img src=\"https://avatars.githubusercontent.com/u/10720298?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Moseco</b></sub></a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/inkomomutane\"><img src=\"https://avatars.githubusercontent.com/u/57417802?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Nelson  Mutane</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Viper-Bit\"><img src=\"https://avatars.githubusercontent.com/u/24822764?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Peyman</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/leisim\"><img src=\"https://avatars.githubusercontent.com/u/13610195?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Simon Leier</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/ika020202\"><img src=\"https://avatars.githubusercontent.com/u/42883378?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Ura</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/blendthink\"><img src=\"https://avatars.githubusercontent.com/u/32213113?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>blendthink</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mnkeis\"><img src=\"https://avatars.githubusercontent.com/u/41247357?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>mnkeis</b></sub></a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/nobkd\"><img src=\"https://avatars.githubusercontent.com/u/44443899?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>nobkd</b></sub></a></td>\n    </tr>\n  </tbody>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\n### License\n\n```\nCopyright 2022 Simon Leier\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n"
  },
  {
    "path": "packages/isar/analysis_options.yaml",
    "content": "include: package:very_good_analysis/analysis_options.yaml\n\nanalyzer:\n  exclude:\n    - \"lib/src/native/bindings.dart\"\n\n  errors:\n    cascade_invocations: ignore\n    avoid_positional_boolean_parameters: ignore\n    parameter_assignments: ignore\n    prefer_asserts_with_message: ignore"
  },
  {
    "path": "packages/isar/example/README.md",
    "content": "## The fastest way to get started is by following the [Quickstart Guide](https://isar.dev/tutorials/quickstart.html)!\n\nHave fun using Isar!"
  },
  {
    "path": "packages/isar/lib/isar.dart",
    "content": "library isar;\n\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:developer';\nimport 'dart:typed_data';\n\nimport 'package:isar/src/isar_connect_api.dart';\nimport 'package:isar/src/native/isar_core.dart'\n    if (dart.library.html) 'package:isar/src/web/isar_web.dart';\nimport 'package:isar/src/native/isar_link_impl.dart'\n    if (dart.library.html) 'package:isar/src/web/isar_link_impl.dart';\nimport 'package:isar/src/native/open.dart'\n    if (dart.library.html) 'package:isar/src/web/open.dart';\nimport 'package:isar/src/native/split_words.dart'\n    if (dart.library.html) 'package:isar/src/web/split_words.dart';\nimport 'package:meta/meta.dart';\nimport 'package:meta/meta_meta.dart';\n\npart 'src/annotations/backlink.dart';\npart 'src/annotations/collection.dart';\npart 'src/annotations/embedded.dart';\npart 'src/annotations/enumerated.dart';\npart 'src/annotations/ignore.dart';\npart 'src/annotations/index.dart';\npart 'src/annotations/name.dart';\npart 'src/annotations/type.dart';\npart 'src/isar.dart';\npart 'src/isar_collection.dart';\npart 'src/isar_connect.dart';\npart 'src/isar_error.dart';\npart 'src/isar_link.dart';\npart 'src/isar_reader.dart';\npart 'src/isar_writer.dart';\npart 'src/query.dart';\npart 'src/query_builder.dart';\npart 'src/query_builder_extensions.dart';\npart 'src/query_components.dart';\npart 'src/schema/collection_schema.dart';\npart 'src/schema/index_schema.dart';\npart 'src/schema/link_schema.dart';\npart 'src/schema/property_schema.dart';\npart 'src/schema/schema.dart';\n\n/// @nodoc\n@protected\ntypedef IsarUint8List = Uint8List;\n\nconst bool _kIsWeb = identical(0, 0.0);\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/backlink.dart",
    "content": "part of isar;\n\n/// Annotation to create a backlink to an existing link.\n@Target({TargetKind.field})\nclass Backlink {\n  /// Annotation to create a backlink to an existing link.\n  const Backlink({required this.to});\n\n  /// The Dart name of the target link.\n  final String to;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/collection.dart",
    "content": "part of isar;\n\n/// Annotation to create an Isar collection.\nconst collection = Collection();\n\n/// Annotation to create an Isar collection.\n@Target({TargetKind.classType})\nclass Collection {\n  /// Annotation to create an Isar collection.\n  const Collection({\n    this.inheritance = true,\n    this.accessor,\n    this.ignore = const {},\n  });\n\n  /// Should properties and accessors of parent classes and mixins be included?\n  final bool inheritance;\n\n  /// Allows you to override the default collection accessor.\n  ///\n  /// Example:\n  /// ```dart\n  /// @Collection(accessor: 'col')\n  /// class MyCol {\n  ///   Id? id;\n  /// }\n  ///\n  /// // access collection using: isar.col\n  /// ```\n  final String? accessor;\n\n  /// A list of properties or getter names that Isar should ignore.\n  final Set<String> ignore;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/embedded.dart",
    "content": "part of isar;\n\n/// Annotation to nest objects of this type in collections.\nconst embedded = Embedded();\n\n/// Annotation to nest objects of this type in collections.\n@Target({TargetKind.classType})\nclass Embedded {\n  /// Annotation to nest objects of this type in collections.\n  const Embedded({this.inheritance = true, this.ignore = const {}});\n\n  /// Should properties and accessors of parent classes and mixins be included?\n  final bool inheritance;\n\n  /// A list of properties or getter names that Isar should ignore.\n  final Set<String> ignore;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/enumerated.dart",
    "content": "part of isar;\n\n/// Annotation to specify how an enum property should be serialized.\nconst enumerated = Enumerated(EnumType.ordinal);\n\n/// Annotation to specify how an enum property should be serialized.\n@Target({TargetKind.field, TargetKind.getter})\nclass Enumerated {\n  /// Annotation to specify how an enum property should be serialized.\n  const Enumerated(this.type, [this.property]);\n\n  /// How the enum property should be serialized.\n  final EnumType type;\n\n  /// The property to use for the enum values.\n  final String? property;\n}\n\n/// Enum type for enum values.\nenum EnumType {\n  /// Stores the index of the enum as a byte value.\n  ordinal,\n\n  /// Stores the index of the enum as a 4-byte value. Use this type if your enum\n  /// has more than 256 values or needs to be nullable.\n  ordinal32,\n\n  /// Uses the name of the enum value.\n  name,\n\n  /// Uses a custom enum value.\n  value\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/ignore.dart",
    "content": "part of isar;\n\n/// Annotate a property or accessor in an Isar collection to ignore it.\nconst ignore = Ignore();\n\n/// Annotate a property or accessor in an Isar collection to ignore it.\n@Target({TargetKind.field, TargetKind.getter})\nclass Ignore {\n  /// Annotate a property or accessor in an Isar collection to ignore it.\n  const Ignore();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/index.dart",
    "content": "part of isar;\n\n/// Specifies how an index is stored in Isar.\nenum IndexType {\n  /// Stores the value as-is in the index.\n  value,\n\n  /// Strings or Lists can be hashed to reduce the storage required by the\n  /// index. The disadvantage of hash indexes is that they can't be used for\n  /// prefix scans (`startsWith()` where clauses). String and list indexes are\n  /// hashed by default.\n  hash,\n\n  /// `List<String>` can hash its elements.\n  hashElements,\n}\n\n/// Annotate properties to build an index.\n@Target({TargetKind.field, TargetKind.getter})\nclass Index {\n  /// Annotate properties to build an index.\n  const Index({\n    this.name,\n    this.composite = const [],\n    this.unique = false,\n    this.replace = false,\n    this.type,\n    this.caseSensitive,\n  });\n\n  /// Name of the index. By default, the names of the properties are\n  /// concatenated using \"_\"\n  final String? name;\n\n  /// Specify up to two other properties to build a composite index.\n  final List<CompositeIndex> composite;\n\n  /// A unique index ensures the index does not contain any duplicate values.\n  /// Any attempt to insert or update data into the unique index that causes a\n  /// duplicate will result in an error.\n  final bool unique;\n\n  /// If set to `true`, inserting a duplicate unique value will replace the\n  /// existing object instead of throwing an error.\n  final bool replace;\n\n  /// Specifies how an index is stored in Isar.\n  ///\n  /// Defaults to:\n  /// - `IndexType.hash` for `String`s and `List`s\n  /// - `IndexType.value` for all other types\n  final IndexType? type;\n\n  /// String or `List<String>` indexes can be case sensitive (default) or case\n  /// insensitive.\n  final bool? caseSensitive;\n}\n\n/// Another property that is part of the composite index.\nclass CompositeIndex {\n  /// Another property that is part of the composite index.\n  const CompositeIndex(\n    this.property, {\n    this.type,\n    this.caseSensitive,\n  });\n\n  /// Dart name of the property.\n  final String property;\n\n  /// See [Index.type].\n  final IndexType? type;\n\n  /// See [Index.caseSensitive].\n  final bool? caseSensitive;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/name.dart",
    "content": "part of isar;\n\n/// Annotate Isar collections or properties to change their name.\n///\n/// Can be used to change the name in Dart independently of Isar.\n@Target({TargetKind.classType, TargetKind.field, TargetKind.getter})\nclass Name {\n  /// Annotate Isar collections or properties to change their name.\n  const Name(this.name);\n\n  /// The name this entity should have in the database.\n  final String name;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/annotations/type.dart",
    "content": "// ignore_for_file: camel_case_types\n\npart of isar;\n\n/// Type to specify the id property of a collection.\ntypedef Id = int;\n\n/// Type to mark an [int] property or List as 8-bit sized.\n///\n/// You may only store values between 0 and 255 in such a property.\ntypedef byte = int;\n\n/// Type to mark an [int] property or List as 32-bit sized.\n///\n/// You may only store values between -2147483648 and 2147483647 in such a\n/// property.\ntypedef short = int;\n\n/// Type to mark a [double] property or List to have 32-bit precision.\ntypedef float = double;\n"
  },
  {
    "path": "packages/isar/lib/src/common/isar_common.dart",
    "content": "// ignore_for_file: invalid_use_of_protected_member\n\nimport 'dart:async';\n\nimport 'package:isar/isar.dart';\n\nconst Symbol _zoneTxn = #zoneTxn;\n\n/// @nodoc\nabstract class IsarCommon extends Isar {\n  /// @nodoc\n  IsarCommon(super.name);\n\n  final List<Future<void>> _activeAsyncTxns = [];\n  var _asyncWriteTxnsActive = 0;\n\n  Transaction? _currentTxnSync;\n\n  void _requireNotInTxn() {\n    if (_currentTxnSync != null || Zone.current[_zoneTxn] != null) {\n      throw IsarError(\n        'Cannot perform this operation from within an active transaction. '\n        'Isar does not support nesting transactions.',\n      );\n    }\n  }\n\n  /// @nodoc\n  Future<Transaction> beginTxn(bool write, bool silent);\n\n  Future<T> _beginTxn<T>(\n    bool write,\n    bool silent,\n    Future<T> Function() callback,\n  ) async {\n    requireOpen();\n    _requireNotInTxn();\n\n    final completer = Completer<void>();\n    _activeAsyncTxns.add(completer.future);\n\n    try {\n      if (write) {\n        _asyncWriteTxnsActive++;\n      }\n\n      final txn = await beginTxn(write, silent);\n\n      final zone = Zone.current.fork(\n        zoneValues: {_zoneTxn: txn},\n      );\n\n      T result;\n      try {\n        result = await zone.run(callback);\n        await txn.commit();\n      } catch (e) {\n        await txn.abort();\n        rethrow;\n      } finally {\n        txn.free();\n      }\n      return result;\n    } finally {\n      completer.complete();\n      _activeAsyncTxns.remove(completer.future);\n      if (write) {\n        _asyncWriteTxnsActive--;\n      }\n    }\n  }\n\n  @override\n  Future<T> txn<T>(Future<T> Function() callback) {\n    return _beginTxn(false, false, callback);\n  }\n\n  @override\n  Future<T> writeTxn<T>(Future<T> Function() callback, {bool silent = false}) {\n    return _beginTxn(true, silent, callback);\n  }\n\n  /// @nodoc\n  Future<R> getTxn<R, T extends Transaction>(\n    bool write,\n    Future<R> Function(T txn) callback,\n  ) {\n    final currentTxn = Zone.current[_zoneTxn] as T?;\n    if (currentTxn != null) {\n      if (!currentTxn.active) {\n        throw IsarError('Transaction is not active anymore. Make sure to await '\n            'all your asynchronous code within transactions to prevent it from '\n            'being closed prematurely.');\n      } else if (write && !currentTxn.write) {\n        throw IsarError('Operation cannot be performed within a read '\n            'transaction. Use isar.writeTxn() instead.');\n      } else if (currentTxn.isar != this) {\n        throw IsarError('Transaction does not match Isar instance. '\n            'Make sure to use transactions from the same Isar instance.');\n      }\n      return callback(currentTxn);\n    } else if (!write) {\n      return _beginTxn(false, false, () {\n        return callback(Zone.current[_zoneTxn] as T);\n      });\n    } else {\n      throw IsarError('Write operations require an explicit transaction. '\n          'Wrap your code in isar.writeTxn()');\n    }\n  }\n\n  /// @nodoc\n  Transaction beginTxnSync(bool write, bool silent);\n\n  T _beginTxnSync<T>(bool write, bool silent, T Function() callback) {\n    requireOpen();\n    _requireNotInTxn();\n\n    if (write && _asyncWriteTxnsActive > 0) {\n      throw IsarError(\n        'An async write transaction is already in progress in this isolate. '\n        'You cannot begin a sync write transaction until it is finished. '\n        'Use asynchroneous transactions if you want to queue multiple write '\n        'transactions.',\n      );\n    }\n\n    final txn = beginTxnSync(write, silent);\n    _currentTxnSync = txn;\n\n    T result;\n    try {\n      result = callback();\n      txn.commitSync();\n    } catch (e) {\n      txn.abortSync();\n      rethrow;\n    } finally {\n      _currentTxnSync = null;\n      txn.free();\n    }\n\n    return result;\n  }\n\n  @override\n  T txnSync<T>(T Function() callback) {\n    return _beginTxnSync(false, false, callback);\n  }\n\n  @override\n  T writeTxnSync<T>(T Function() callback, {bool silent = false}) {\n    return _beginTxnSync(true, silent, callback);\n  }\n\n  /// @nodoc\n  R getTxnSync<R, T extends Transaction>(\n    bool write,\n    R Function(T txn) callback,\n  ) {\n    if (_currentTxnSync != null) {\n      if (write && !_currentTxnSync!.write) {\n        throw IsarError(\n          'Operation cannot be performed within a read transaction. '\n          'Use isar.writeTxnSync() instead.',\n        );\n      }\n      return callback(_currentTxnSync! as T);\n    } else if (!write) {\n      return _beginTxnSync(false, false, () => callback(_currentTxnSync! as T));\n    } else {\n      throw IsarError('Write operations require an explicit transaction. '\n          'Wrap your code in isar.writeTxnSync()');\n    }\n  }\n\n  @override\n  Future<bool> close({bool deleteFromDisk = false}) async {\n    requireOpen();\n    _requireNotInTxn();\n    await Future.wait(_activeAsyncTxns);\n    await super.close();\n\n    return performClose(deleteFromDisk);\n  }\n\n  /// @nodoc\n  bool performClose(bool deleteFromDisk);\n}\n\n/// @nodoc\nabstract class Transaction {\n  /// @nodoc\n  Transaction(this.isar, this.sync, this.write);\n\n  /// @nodoc\n  final Isar isar;\n\n  /// @nodoc\n  final bool sync;\n\n  /// @nodoc\n  final bool write;\n\n  /// @nodoc\n  bool get active;\n\n  /// @nodoc\n  Future<void> commit();\n\n  /// @nodoc\n  void commitSync();\n\n  /// @nodoc\n  Future<void> abort();\n\n  /// @nodoc\n  void abortSync();\n\n  /// @nodoc\n  void free() {}\n}\n"
  },
  {
    "path": "packages/isar/lib/src/common/isar_link_base_impl.dart",
    "content": "import 'package:isar/isar.dart';\n\n/// @nodoc\nabstract class IsarLinkBaseImpl<OBJ> implements IsarLinkBase<OBJ> {\n  var _initialized = false;\n\n  Id? _objectId;\n\n  /// The isar name of the link\n  late final String linkName;\n\n  /// The origin collection of the link. For backlinks it is actually the target\n  /// collection.\n  late final IsarCollection<dynamic> sourceCollection;\n\n  /// The target collection of the link. For backlinks it is actually the origin\n  /// collection.\n  late final IsarCollection<OBJ> targetCollection;\n\n  @override\n  bool get isAttached => _objectId != null;\n\n  @override\n  void attach(\n    IsarCollection<dynamic> sourceCollection,\n    IsarCollection<OBJ> targetCollection,\n    String linkName,\n    Id? objectId,\n  ) {\n    if (_initialized) {\n      if (linkName != this.linkName ||\n          !identical(sourceCollection, this.sourceCollection) ||\n          !identical(targetCollection, this.targetCollection)) {\n        throw IsarError(\n          'Link has been moved! It is not allowed to move '\n          'a link to a different collection.',\n        );\n      }\n    } else {\n      _initialized = true;\n      this.sourceCollection = sourceCollection;\n      this.targetCollection = targetCollection;\n      this.linkName = linkName;\n    }\n\n    _objectId = objectId;\n  }\n\n  /// Returns the containing object's id or throws an exception if this link has\n  /// not been attached to an object yet.\n  Id requireAttached() {\n    if (_objectId == null) {\n      throw IsarError(\n        'Containing object needs to be managed by Isar to use this method. '\n        'Use collection.put(yourObject) to add it to the database.',\n      );\n    } else {\n      return _objectId!;\n    }\n  }\n\n  /// Returns the id of a linked object.\n  Id Function(OBJ obj) get getId;\n\n  /// Returns the id of a linked object or throws an exception if the id is\n  /// `null` or set to `Isar.autoIncrement`.\n  Id requireGetId(OBJ object) {\n    final id = getId(object);\n    if (id != Isar.autoIncrement) {\n      return id;\n    } else {\n      throw IsarError(\n        'Object \"$object\" has no id and can therefore not be linked. '\n        'Make sure to .put() objects before you use them in links.',\n      );\n    }\n  }\n\n  /// See [IsarLinks.filter].\n  QueryBuilder<OBJ, OBJ, QAfterFilterCondition> filter() {\n    final containingId = requireAttached();\n    final qb = QueryBuilderInternal(\n      collection: targetCollection,\n      whereClauses: [\n        LinkWhereClause(\n          linkCollection: sourceCollection.name,\n          linkName: linkName,\n          id: containingId,\n        ),\n      ],\n    );\n    return QueryBuilder(qb);\n  }\n\n  /// See [IsarLinks.update].\n  Future<void> update({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  });\n\n  /// See [IsarLinks.updateSync].\n  void updateSync({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  });\n}\n"
  },
  {
    "path": "packages/isar/lib/src/common/isar_link_common.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar/src/common/isar_link_base_impl.dart';\n\nconst bool _kIsWeb = identical(0, 0.0);\n\n/// @nodoc\nabstract class IsarLinkCommon<OBJ> extends IsarLinkBaseImpl<OBJ>\n    with IsarLink<OBJ> {\n  OBJ? _value;\n\n  @override\n  bool isChanged = false;\n\n  @override\n  bool isLoaded = false;\n\n  @override\n  OBJ? get value {\n    if (isAttached && !isLoaded && !isChanged && !_kIsWeb) {\n      loadSync();\n    }\n    return _value;\n  }\n\n  @override\n  set value(OBJ? value) {\n    isChanged |= !identical(_value, value);\n    _value = value;\n    isLoaded = true;\n  }\n\n  @override\n  Future<void> load() async {\n    _value = await filter().findFirst();\n    isChanged = false;\n    isLoaded = true;\n  }\n\n  @override\n  void loadSync() {\n    _value = filter().findFirstSync();\n    isChanged = false;\n    isLoaded = true;\n  }\n\n  @override\n  Future<void> save() async {\n    if (!isChanged) {\n      return;\n    }\n\n    final object = value;\n\n    await update(link: [if (object != null) object], reset: true);\n    isChanged = false;\n    isLoaded = true;\n  }\n\n  @override\n  void saveSync() {\n    if (!isChanged) {\n      return;\n    }\n\n    final object = _value;\n    updateSync(link: [if (object != null) object], reset: true);\n\n    isChanged = false;\n    isLoaded = true;\n  }\n\n  @override\n  Future<void> reset() async {\n    await update(reset: true);\n    _value = null;\n    isChanged = false;\n    isLoaded = true;\n  }\n\n  @override\n  void resetSync() {\n    updateSync(reset: true);\n    _value = null;\n    isChanged = false;\n    isLoaded = true;\n  }\n\n  @override\n  String toString() {\n    return 'IsarLink($_value)';\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/common/isar_links_common.dart",
    "content": "import 'dart:collection';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/common/isar_link_base_impl.dart';\n\nconst bool _kIsWeb = identical(0, 0.0);\n\n/// @nodoc\nabstract class IsarLinksCommon<OBJ> extends IsarLinkBaseImpl<OBJ>\n    with IsarLinks<OBJ>, SetMixin<OBJ> {\n  final _objects = <Id, OBJ>{};\n\n  /// @nodoc\n  final addedObjects = HashSet<OBJ>.identity();\n\n  /// @nodoc\n  final removedObjects = HashSet<OBJ>.identity();\n\n  @override\n  bool isLoaded = false;\n\n  @override\n  bool get isChanged => addedObjects.isNotEmpty || removedObjects.isNotEmpty;\n\n  Map<Id, OBJ> get _loadedObjects {\n    if (isAttached && !isLoaded && !_kIsWeb) {\n      loadSync();\n    }\n    return _objects;\n  }\n\n  @override\n  void attach(\n    IsarCollection<dynamic> sourceCollection,\n    IsarCollection<OBJ> targetCollection,\n    String linkName,\n    Id? objectId,\n  ) {\n    super.attach(sourceCollection, targetCollection, linkName, objectId);\n\n    _applyAddedRemoved();\n  }\n\n  @override\n  Future<void> load({bool overrideChanges = false}) async {\n    final objects = await filter().findAll();\n    _applyLoaded(objects, overrideChanges);\n  }\n\n  @override\n  void loadSync({bool overrideChanges = false}) {\n    final objects = filter().findAllSync();\n    _applyLoaded(objects, overrideChanges);\n  }\n\n  void _applyLoaded(List<OBJ> objects, bool overrideChanges) {\n    _objects.clear();\n    for (final object in objects) {\n      final id = getId(object);\n      if (id != Isar.autoIncrement) {\n        _objects[id] = object;\n      }\n    }\n\n    if (overrideChanges) {\n      addedObjects.clear();\n      removedObjects.clear();\n    } else {\n      _applyAddedRemoved();\n    }\n\n    isLoaded = true;\n  }\n\n  void _applyAddedRemoved() {\n    for (final object in addedObjects) {\n      final id = getId(object);\n      if (id != Isar.autoIncrement) {\n        _objects[id] = object;\n      }\n    }\n\n    for (final object in removedObjects) {\n      final id = getId(object);\n      if (id != Isar.autoIncrement) {\n        _objects.remove(id);\n      }\n    }\n  }\n\n  @override\n  Future<void> save() async {\n    if (!isChanged) {\n      return;\n    }\n\n    await update(link: addedObjects, unlink: removedObjects);\n\n    addedObjects.clear();\n    removedObjects.clear();\n    isLoaded = true;\n  }\n\n  @override\n  void saveSync() {\n    if (!isChanged) {\n      return;\n    }\n\n    updateSync(link: addedObjects, unlink: removedObjects);\n\n    addedObjects.clear();\n    removedObjects.clear();\n    isLoaded = true;\n  }\n\n  @override\n  Future<void> reset() async {\n    await update(reset: true);\n    clear();\n    isLoaded = true;\n  }\n\n  @override\n  void resetSync() {\n    updateSync(reset: true);\n    clear();\n    isLoaded = true;\n  }\n\n  @override\n  bool add(OBJ value) {\n    if (isAttached) {\n      final id = getId(value);\n      if (id != Isar.autoIncrement) {\n        if (_objects.containsKey(id)) {\n          return false;\n        }\n        _objects[id] = value;\n      }\n    }\n\n    removedObjects.remove(value);\n    return addedObjects.add(value);\n  }\n\n  @override\n  bool contains(Object? element) {\n    requireAttached();\n\n    if (element is OBJ) {\n      final id = getId(element);\n      if (id != Isar.autoIncrement) {\n        return _loadedObjects.containsKey(id);\n      }\n    }\n    return false;\n  }\n\n  @override\n  Iterator<OBJ> get iterator => _loadedObjects.values.iterator;\n\n  @override\n  int get length => _loadedObjects.length;\n\n  @override\n  OBJ? lookup(Object? element) {\n    requireAttached();\n\n    if (element is OBJ) {\n      final id = getId(element);\n      if (id != Isar.autoIncrement) {\n        return _loadedObjects[id];\n      }\n    }\n    return null;\n  }\n\n  @override\n  bool remove(Object? value) {\n    if (value is! OBJ) {\n      return false;\n    }\n\n    if (isAttached) {\n      final id = getId(value);\n      if (id != Isar.autoIncrement) {\n        if (isLoaded && !_objects.containsKey(id)) {\n          return false;\n        }\n        _objects.remove(id);\n      }\n    }\n\n    addedObjects.remove(value);\n    return removedObjects.add(value);\n  }\n\n  @override\n  Set<OBJ> toSet() {\n    requireAttached();\n    return HashSet(\n      equals: (o1, o2) => getId(o1) == getId(o2),\n      // ignore: noop_primitive_operations\n      hashCode: (o) => getId(o).toInt(),\n      isValidKey: (o) => o is OBJ && getId(o) != Isar.autoIncrement,\n    )..addAll(_loadedObjects.values);\n  }\n\n  @override\n  void clear() {\n    _objects.clear();\n    addedObjects.clear();\n    removedObjects.clear();\n  }\n\n  @override\n  String toString() {\n    final content =\n        IterableBase.iterableToFullString(_objects.values, '{', '}');\n    return 'IsarLinks($content)';\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/common/schemas.dart",
    "content": "import 'package:isar/isar.dart';\n\n/// @nodoc\nList<Schema<dynamic>> getSchemas(\n  List<CollectionSchema<dynamic>> collectionSchemas,\n) {\n  final schemas = <Schema<dynamic>>{};\n  for (final collectionSchema in collectionSchemas) {\n    schemas.add(collectionSchema);\n    schemas.addAll(collectionSchema.embeddedSchemas.values);\n  }\n  return schemas.toList();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar.dart",
    "content": "part of isar;\n\n/// Callback for a newly opened Isar instance.\ntypedef IsarOpenCallback = void Function(Isar isar);\n\n/// Callback for a release Isar instance.\ntypedef IsarCloseCallback = void Function(String isarName);\n\n/// An instance of the Isar Database.\nabstract class Isar {\n  /// @nodoc\n  @protected\n  Isar(this.name) {\n    _instances[name] = this;\n    for (final callback in _openCallbacks) {\n      callback(this);\n    }\n  }\n\n  /// The version of the Isar library.\n  static const version = '3.1.8';\n\n  /// Smallest valid id.\n  static const Id minId = isarMinId;\n\n  /// Largest valid id.\n  static const Id maxId = isarMaxId;\n\n  /// The default Isar instance name.\n  static const String defaultName = 'default';\n\n  /// The default max Isar size.\n  static const int defaultMaxSizeMiB = 1024;\n\n  /// Placeholder for an auto-increment id.\n  static const Id autoIncrement = isarAutoIncrementId;\n\n  static final Map<String, Isar> _instances = <String, Isar>{};\n  static final Set<IsarOpenCallback> _openCallbacks = <IsarOpenCallback>{};\n  static final Set<IsarCloseCallback> _closeCallbacks = <IsarCloseCallback>{};\n\n  /// Name of the instance.\n  final String name;\n\n  /// The directory containing the database file or `null` on the web.\n  String? get directory;\n\n  /// The full path of the database file is `directory/name.isar` and the lock\n  /// file `directory/name.isar.lock`.\n  String? get path => directory != null ? '$directory/$name.isar' : null;\n\n  late final Map<Type, IsarCollection<dynamic>> _collections;\n  late final Map<String, IsarCollection<dynamic>> _collectionsByName;\n\n  bool _isOpen = true;\n\n  static void _checkOpen(String name, List<CollectionSchema<dynamic>> schemas) {\n    if (name.isEmpty || name.startsWith('_')) {\n      throw IsarError('Instance names must not be empty or start with \"_\".');\n    }\n    if (_instances.containsKey(name)) {\n      throw IsarError('Instance has already been opened.');\n    }\n    if (schemas.isEmpty) {\n      throw IsarError('At least one collection needs to be opened.');\n    }\n\n    final schemaNames = <String>{};\n    for (final schema in schemas) {\n      if (!schemaNames.add(schema.name)) {\n        throw IsarError('Duplicate collection ${schema.name}.');\n      }\n    }\n    for (final schema in schemas) {\n      final dependencies = schema.links.values.map((e) => e.target);\n      for (final dependency in dependencies) {\n        if (!schemaNames.contains(dependency)) {\n          throw IsarError(\n            \"Collection ${schema.name} depends on $dependency but it's schema \"\n            'was not provided.',\n          );\n        }\n      }\n    }\n  }\n\n  /// Open a new Isar instance.\n  static Future<Isar> open(\n    List<CollectionSchema<dynamic>> schemas, {\n    required String directory,\n    String name = defaultName,\n    int maxSizeMiB = Isar.defaultMaxSizeMiB,\n    bool relaxedDurability = true,\n    CompactCondition? compactOnLaunch,\n    bool inspector = true,\n  }) {\n    _checkOpen(name, schemas);\n\n    /// Tree shake the inspector for profile and release builds.\n    assert(() {\n      if (!_kIsWeb && inspector) {\n        _IsarConnect.initialize(schemas);\n      }\n      return true;\n    }());\n\n    return openIsar(\n      schemas: schemas,\n      directory: directory,\n      name: name,\n      maxSizeMiB: maxSizeMiB,\n      relaxedDurability: relaxedDurability,\n      compactOnLaunch: compactOnLaunch,\n    );\n  }\n\n  /// Open a new Isar instance.\n  static Isar openSync(\n    List<CollectionSchema<dynamic>> schemas, {\n    required String directory,\n    String name = defaultName,\n    int maxSizeMiB = Isar.defaultMaxSizeMiB,\n    bool relaxedDurability = true,\n    CompactCondition? compactOnLaunch,\n    bool inspector = true,\n  }) {\n    _checkOpen(name, schemas);\n\n    /// Tree shake the inspector for profile and release builds.\n    assert(() {\n      if (!_kIsWeb && inspector) {\n        _IsarConnect.initialize(schemas);\n      }\n      return true;\n    }());\n\n    return openIsarSync(\n      schemas: schemas,\n      directory: directory,\n      name: name,\n      maxSizeMiB: maxSizeMiB,\n      relaxedDurability: relaxedDurability,\n      compactOnLaunch: compactOnLaunch,\n    );\n  }\n\n  /// Is the instance open?\n  bool get isOpen => _isOpen;\n\n  /// @nodoc\n  @protected\n  void requireOpen() {\n    if (!isOpen) {\n      throw IsarError('Isar instance has already been closed');\n    }\n  }\n\n  /// Executes an asynchronous read-only transaction.\n  Future<T> txn<T>(Future<T> Function() callback);\n\n  /// Executes an asynchronous read-write transaction.\n  ///\n  /// If [silent] is `true`, watchers are not notified about changes in this\n  /// transaction.\n  Future<T> writeTxn<T>(Future<T> Function() callback, {bool silent = false});\n\n  /// Executes a synchronous read-only transaction.\n  T txnSync<T>(T Function() callback);\n\n  /// Executes a synchronous read-write transaction.\n  ///\n  /// If [silent] is `true`, watchers are not notified about changes in this\n  /// transaction.\n  T writeTxnSync<T>(T Function() callback, {bool silent = false});\n\n  /// @nodoc\n  @protected\n  void attachCollections(Map<Type, IsarCollection<dynamic>> collections) {\n    _collections = collections;\n    _collectionsByName = {\n      for (IsarCollection<dynamic> col in collections.values) col.name: col,\n    };\n  }\n\n  /// Get a collection by its type.\n  ///\n  /// You should use the generated extension methods instead.\n  IsarCollection<T> collection<T>() {\n    requireOpen();\n    final collection = _collections[T];\n    if (collection == null) {\n      throw IsarError('Missing ${T.runtimeType}Schema in Isar.open');\n    }\n    return collection as IsarCollection<T>;\n  }\n\n  /// @nodoc\n  @protected\n  IsarCollection<dynamic>? getCollectionByNameInternal(String name) {\n    return _collectionsByName[name];\n  }\n\n  /// Remove all data in this instance and reset the auto increment values.\n  Future<void> clear() async {\n    for (final col in _collections.values) {\n      await col.clear();\n    }\n  }\n\n  /// Remove all data in this instance and reset the auto increment values.\n  void clearSync() {\n    for (final col in _collections.values) {\n      col.clearSync();\n    }\n  }\n\n  /// Returns the size of all the collections in bytes. Not supported on web.\n  ///\n  /// This method is extremely fast and independent of the number of objects in\n  /// the instance.\n  Future<int> getSize({bool includeIndexes = false, bool includeLinks = false});\n\n  /// Returns the size of all collections in bytes. Not supported on web.\n  ///\n  /// This method is extremely fast and independent of the number of objects in\n  /// the instance.\n  int getSizeSync({bool includeIndexes = false, bool includeLinks = false});\n\n  /// Copy a compacted version of the database to the specified file.\n  ///\n  /// If you want to backup your database, you should always use a compacted\n  /// version. Compacted does not mean compressed.\n  ///\n  /// Do not run this method while other transactions are active to avoid\n  /// unnecessary growth of the database.\n  Future<void> copyToFile(String targetPath);\n\n  /// Releases an Isar instance.\n  ///\n  /// If this is the only isolate that holds a reference to this instance, the\n  /// Isar instance will be closed. [deleteFromDisk] additionally removes all\n  /// database files if enabled.\n  ///\n  /// Returns whether the instance was actually closed.\n  Future<bool> close({bool deleteFromDisk = false}) {\n    requireOpen();\n    _isOpen = false;\n    if (identical(_instances[name], this)) {\n      _instances.remove(name);\n    }\n    for (final callback in _closeCallbacks) {\n      callback(name);\n    }\n    return Future.value(false);\n  }\n\n  /// Verifies the integrity of the database file.\n  ///\n  /// Do not use this method in production apps.\n  @visibleForTesting\n  @experimental\n  Future<void> verify();\n\n  /// A list of all Isar instances opened in the current isolate.\n  static Set<String> get instanceNames => _instances.keys.toSet();\n\n  /// Returns an Isar instance opened in the current isolate by its name. If\n  /// no name is provided, the default instance is returned.\n  static Isar? getInstance([String name = defaultName]) {\n    return _instances[name];\n  }\n\n  /// Registers a listener that is called whenever an Isar instance is opened.\n  static void addOpenListener(IsarOpenCallback callback) {\n    _openCallbacks.add(callback);\n  }\n\n  /// Removes a previously registered `IsarOpenCallback`.\n  static void removeOpenListener(IsarOpenCallback callback) {\n    _openCallbacks.remove(callback);\n  }\n\n  /// Registers a listener that is called whenever an Isar instance is\n  /// released.\n  static void addCloseListener(IsarCloseCallback callback) {\n    _closeCallbacks.add(callback);\n  }\n\n  /// Removes a previously registered `IsarOpenCallback`.\n  static void removeCloseListener(IsarCloseCallback callback) {\n    _closeCallbacks.remove(callback);\n  }\n\n  /// Initialize Isar Core manually. You need to provide Isar Core libraries\n  /// for every platform your app will run on.\n  ///\n  /// If [download] is `true`, Isar will attempt to download the correct\n  /// library and place it in the specified path or the script directory.\n  ///\n  /// Be careful if multiple unit tests try to download the library at the\n  /// same time. Always use `flutter test -j 1` when you rely on auto\n  /// downloading to ensure that only one test is running at a time.\n  ///\n  /// Only use this method for non-Flutter code or unit tests.\n  static Future<void> initializeIsarCore({\n    Map<IsarAbi, String> libraries = const {},\n    bool download = false,\n  }) async {\n    await initializeCoreBinary(\n      libraries: libraries,\n      download: download,\n    );\n  }\n\n  /// Split a String into words according to Unicode Annex #29. Only words\n  /// containing at least one alphanumeric character will be included.\n  static List<String> splitWords(String input) => isarSplitWords(input);\n}\n\n/// Isar databases can contain unused space that will be reused for later\n/// operations. You can specify conditions to trigger manual compaction where\n/// the entire database is copied and unused space freed.\n///\n/// This operation can only be performed while a database is being opened and\n/// should only be used if absolutely necessary.\nclass CompactCondition {\n  /// Compaction will happen if all of the specified conditions are true.\n  const CompactCondition({\n    this.minFileSize,\n    this.minBytes,\n    this.minRatio,\n  }) : assert(\n          minFileSize != null || minBytes != null || minRatio != null,\n          'At least one condition needs to be specified.',\n        );\n\n  /// The minimum size in bytes of the database file to trigger compaction. It\n  /// is highly  discouraged to trigger compaction solely on this condition.\n  final int? minFileSize;\n\n  /// The minimum number of bytes that can be freed with compaction.\n  final int? minBytes;\n\n  /// The minimum compaction ration. For example `2.0` would trigger compaction\n  /// as soon as the file size can be halved.\n  final double? minRatio;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_collection.dart",
    "content": "part of isar;\n\n/// Normal keys consist of a single object, composite keys multiple.\ntypedef IndexKey = List<Object?>;\n\n/// Use `IsarCollection` instances to find, query, and create new objects of a\n/// given type in Isar.\n///\n/// You can get an instance of `IsarCollection` by calling `isar.get<OBJ>()` or\n/// by using the generated `isar.yourCollections` getter.\nabstract class IsarCollection<OBJ> {\n  /// The corresponding Isar instance.\n  Isar get isar;\n\n  /// Get the schema of the collection.\n  CollectionSchema<OBJ> get schema;\n\n  /// The name of the collection.\n  String get name => schema.name;\n\n  /// {@template col_get}\n  /// Get a single object by its [id] or `null` if the object does not exist.\n  /// {@endtemplate}\n  Future<OBJ?> get(Id id) {\n    return getAll([id]).then((List<OBJ?> objects) => objects[0]);\n  }\n\n  /// {@macro col_get}\n  OBJ? getSync(Id id) {\n    return getAllSync([id])[0];\n  }\n\n  /// {@template col_get_all}\n  /// Get a list of objects by their [ids] or `null` if an object does not\n  /// exist.\n  /// {@endtemplate}\n  Future<List<OBJ?>> getAll(List<Id> ids);\n\n  /// {@macro col_get_all}\n  List<OBJ?> getAllSync(List<Id> ids);\n\n  /// {@template col_get_by_index}\n  /// Get a single object by the unique index [indexName] and [key].\n  ///\n  /// Returns `null` if the object does not exist.\n  ///\n  /// If possible, you should use the generated type-safe methods instead.\n  /// {@endtemplate}\n  @experimental\n  Future<OBJ?> getByIndex(String indexName, IndexKey key) {\n    return getAllByIndex(indexName, [key])\n        .then((List<OBJ?> objects) => objects[0]);\n  }\n\n  /// {@macro col_get_by_index}\n  @experimental\n  OBJ? getByIndexSync(String indexName, IndexKey key) {\n    return getAllByIndexSync(indexName, [key])[0];\n  }\n\n  /// {@template col_get_all_by_index}\n  /// Get a list of objects by the unique index [indexName] and [keys].\n  ///\n  /// Returns `null` if the object does not exist.\n  ///\n  /// If possible, you should use the generated type-safe methods instead.\n  /// {@endtemplate}\n  @experimental\n  Future<List<OBJ?>> getAllByIndex(String indexName, List<IndexKey> keys);\n\n  /// {@macro col_get_all_by_index}'\n  @experimental\n  List<OBJ?> getAllByIndexSync(String indexName, List<IndexKey> keys);\n\n  /// {@template col_put}\n  /// Insert or update an [object]. Returns the id of the new or updated object.\n  ///\n  /// If the object has an non-final id property, it will be set to the assigned\n  /// id. Otherwise you should use the returned id to update the object.\n  /// {@endtemplate}\n  Future<Id> put(OBJ object) {\n    return putAll([object]).then((List<Id> ids) => ids[0]);\n  }\n\n  /// {@macro col_put}\n  Id putSync(OBJ object, {bool saveLinks = true}) {\n    return putAllSync([object], saveLinks: saveLinks)[0];\n  }\n\n  /// {@template col_put_all}\n  /// Insert or update a list of [objects]. Returns the list of ids of the new\n  /// or updated objects.\n  ///\n  /// If the objects have an non-final id property, it will be set to the\n  /// assigned id. Otherwise you should use the returned ids to update the\n  /// objects.\n  /// {@endtemplate}\n  Future<List<Id>> putAll(List<OBJ> objects);\n\n  /// {@macro col_put_all}\n  List<Id> putAllSync(List<OBJ> objects, {bool saveLinks = true});\n\n  /// {@template col_put_by_index}\n  /// Insert or update the [object] by the unique index [indexName]. Returns the\n  /// id of the new or updated object.\n  ///\n  /// If there is already an object with the same index key, it will be\n  /// updated and all links will be preserved. Otherwise a new object will be\n  /// inserted.\n  ///\n  /// If the object has an non-final id property, it will be set to the assigned\n  /// id. Otherwise you should use the returned id to update the object.\n  ///\n  /// If possible, you should use the generated type-safe methods instead.\n  /// {@endtemplate}\n  @experimental\n  Future<Id> putByIndex(String indexName, OBJ object) {\n    return putAllByIndex(indexName, [object]).then((List<Id> ids) => ids[0]);\n  }\n\n  /// {@macro col_put_by_index}\n  @experimental\n  Id putByIndexSync(String indexName, OBJ object, {bool saveLinks = true}) {\n    return putAllByIndexSync(indexName, [object])[0];\n  }\n\n  /// {@template col_put_all_by_index}\n  /// Insert or update a list of [objects] by the unique index [indexName].\n  /// Returns the list of ids of the new or updated objects.\n  ///\n  /// If there is already an object with the same index key, it will be\n  /// updated and all links will be preserved. Otherwise a new object will be\n  /// inserted.\n  ///\n  /// If the objects have an non-final id property, it will be set to the\n  /// assigned id. Otherwise you should use the returned ids to update the\n  /// objects.\n  ///\n  /// If possible, you should use the generated type-safe methods instead.\n  /// {@endtemplate}\n  @experimental\n  Future<List<Id>> putAllByIndex(String indexName, List<OBJ> objects);\n\n  /// {@macro col_put_all_by_index}\n  @experimental\n  List<Id> putAllByIndexSync(\n    String indexName,\n    List<OBJ> objects, {\n    bool saveLinks = true,\n  });\n\n  /// {@template col_delete}\n  /// Delete a single object by its [id].\n  ///\n  /// Returns whether the object has been deleted. Isar web always returns\n  /// `true`.\n  /// {@endtemplate}\n  Future<bool> delete(Id id) {\n    return deleteAll([id]).then((int count) => count == 1);\n  }\n\n  /// {@macro col_delete}\n  bool deleteSync(Id id) {\n    return deleteAllSync([id]) == 1;\n  }\n\n  /// {@template col_delete_all}\n  /// Delete a list of objects by their [ids].\n  ///\n  /// Returns the number of objects that have been deleted. Isar web always\n  /// returns `ids.length`.\n  /// {@endtemplate}\n  Future<int> deleteAll(List<Id> ids);\n\n  /// {@macro col_delete_all}\n  int deleteAllSync(List<Id> ids);\n\n  /// {@template col_delete_by_index}\n  /// Delete a single object by the unique index [indexName] and [key].\n  ///\n  /// Returns whether the object has been deleted. Isar web always returns\n  /// `true`.\n  /// {@endtemplate}\n  @experimental\n  Future<bool> deleteByIndex(String indexName, IndexKey key) {\n    return deleteAllByIndex(indexName, [key]).then((int count) => count == 1);\n  }\n\n  /// {@macro col_delete_by_index}\n  @experimental\n  bool deleteByIndexSync(String indexName, IndexKey key) {\n    return deleteAllByIndexSync(indexName, [key]) == 1;\n  }\n\n  /// {@template col_delete_all_by_index}\n  /// Delete a list of objects by the unique index [indexName] and [keys].\n  ///\n  /// Returns the number of objects that have been deleted. Isar web always\n  /// returns `keys.length`.\n  /// {@endtemplate}\n  @experimental\n  Future<int> deleteAllByIndex(String indexName, List<IndexKey> keys);\n\n  /// {@macro col_delete_all_by_index}\n  @experimental\n  int deleteAllByIndexSync(String indexName, List<IndexKey> keys);\n\n  /// {@template col_clear}\n  /// Remove all data in this collection and reset the auto increment value.\n  /// {@endtemplate}\n  Future<void> clear();\n\n  /// {@macro col_clear}\n  void clearSync();\n\n  /// {@template col_import_json_raw}\n  /// Import a list of json objects encoded as a byte array.\n  ///\n  /// The json objects must have the same structure as the objects in this\n  /// collection. Otherwise an exception will be thrown.\n  /// {@endtemplate}\n  Future<void> importJsonRaw(Uint8List jsonBytes);\n\n  /// {@macro col_import_json_raw}\n  void importJsonRawSync(Uint8List jsonBytes);\n\n  /// {@template col_import_json}\n  /// Import a list of json objects.\n  ///\n  /// The json objects must have the same structure as the objects in this\n  /// collection. Otherwise an exception will be thrown.\n  /// {@endtemplate}\n  Future<void> importJson(List<Map<String, dynamic>> json);\n\n  /// {@macro col_import_json}\n  void importJsonSync(List<Map<String, dynamic>> json);\n\n  /// Start building a query using the [QueryBuilder].\n  ///\n  /// You can use where clauses to only return [distinct] results. If you want\n  /// to reverse the order, set [sort] to [Sort.desc].\n  QueryBuilder<OBJ, OBJ, QWhere> where({\n    bool distinct = false,\n    Sort sort = Sort.asc,\n  }) {\n    final qb = QueryBuilderInternal(\n      collection: this,\n      whereDistinct: distinct,\n      whereSort: sort,\n    );\n    return QueryBuilder(qb);\n  }\n\n  /// Start building a query using the [QueryBuilder].\n  ///\n  /// Shortcut if you don't want to use where clauses.\n  QueryBuilder<OBJ, OBJ, QFilterCondition> filter() => where().filter();\n\n  /// Build a query dynamically for example to build a custom query language.\n  ///\n  /// It is highly discouraged to use this method. Only in very special cases\n  /// should it be used. If you open an issue please always mention that you\n  /// used this method.\n  ///\n  /// The type argument [R] needs to be equal to [OBJ] if no [property] is\n  /// specified. Otherwise it should be the type of the property.\n  @experimental\n  Query<R> buildQuery<R>({\n    List<WhereClause> whereClauses = const [],\n    bool whereDistinct = false,\n    Sort whereSort = Sort.asc,\n    FilterOperation? filter,\n    List<SortProperty> sortBy = const [],\n    List<DistinctProperty> distinctBy = const [],\n    int? offset,\n    int? limit,\n    String? property,\n  });\n\n  /// {@template col_count}\n  /// Returns the total number of objects in this collection.\n  ///\n  /// For non-web apps, this method is extremely fast and independent of the\n  /// number of objects in the collection.\n  /// {@endtemplate}\n  Future<int> count();\n\n  /// {@macro col_count}\n  int countSync();\n\n  /// {@template col_size}\n  /// Returns the size of the collection in bytes. Not supported on web.\n  ///\n  /// For non-web apps, this method is extremely fast and independent of the\n  /// number of objects in the collection.\n  /// {@endtemplate}\n  Future<int> getSize({bool includeIndexes = false, bool includeLinks = false});\n\n  /// {@macro col_size}\n  int getSizeSync({bool includeIndexes = false, bool includeLinks = false});\n\n  /// Watch the collection for changes.\n  ///\n  /// If [fireImmediately] is `true`, an event will be fired immediately.\n  Stream<void> watchLazy({bool fireImmediately = false});\n\n  /// Watch the object with [id] for changes. If a change occurs, the new object\n  /// will be returned in the stream.\n  ///\n  /// Objects that don't exist (yet) can also be watched. If [fireImmediately]\n  /// is `true`, the object will be sent to the consumer immediately.\n  Stream<OBJ?> watchObject(Id id, {bool fireImmediately = false});\n\n  /// Watch the object with [id] for changes.\n  ///\n  /// If [fireImmediately] is `true`, an event will be fired immediately.\n  Stream<void> watchObjectLazy(Id id, {bool fireImmediately = false});\n\n  /// Verifies the integrity of the collection and its indexes.\n  ///\n  /// Throws an exception if the collection does not contain exactly the\n  /// provided [objects].\n  ///\n  /// Do not use this method in production apps.\n  @visibleForTesting\n  @experimental\n  Future<void> verify(List<OBJ> objects);\n\n  /// Verifies the integrity of a link.\n  ///\n  /// Throws an exception if not exactly [sourceIds] as linked to the\n  /// [targetIds].\n  ///\n  /// Do not use this method in production apps.\n  @visibleForTesting\n  @experimental\n  Future<void> verifyLink(\n    String linkName,\n    List<int> sourceIds,\n    List<int> targetIds,\n  );\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_connect.dart",
    "content": "// coverage:ignore-file\n// ignore_for_file: avoid_print\n\npart of isar;\n\nabstract class _IsarConnect {\n  static const Map<ConnectAction,\n      Future<dynamic> Function(Map<String, dynamic> _)> _handlers = {\n    ConnectAction.getSchema: _getSchema,\n    ConnectAction.listInstances: _listInstances,\n    ConnectAction.watchInstance: _watchInstance,\n    ConnectAction.executeQuery: _executeQuery,\n    ConnectAction.removeQuery: _removeQuery,\n    ConnectAction.importJson: _importJson,\n    ConnectAction.exportJson: _exportJson,\n    ConnectAction.editProperty: _editProperty,\n  };\n\n  static List<CollectionSchema<dynamic>>? _schemas;\n\n  // ignore: cancel_subscriptions\n  static final _querySubscription = <StreamSubscription<void>>[];\n  static final List<StreamSubscription<void>> _collectionSubscriptions =\n      <StreamSubscription<void>>[];\n\n  static void initialize(List<CollectionSchema<dynamic>> schemas) {\n    if (_schemas != null) {\n      return;\n    }\n    _schemas = schemas;\n\n    Isar.addOpenListener((_) {\n      postEvent(ConnectEvent.instancesChanged.event, {});\n    });\n\n    Isar.addCloseListener((_) {\n      postEvent(ConnectEvent.instancesChanged.event, {});\n    });\n\n    for (final handler in _handlers.entries) {\n      registerExtension(handler.key.method,\n          (String method, Map<String, String> parameters) async {\n        try {\n          final args = parameters.containsKey('args')\n              ? jsonDecode(parameters['args']!) as Map<String, dynamic>\n              : <String, dynamic>{};\n          final result = <String, dynamic>{'result': await handler.value(args)};\n          return ServiceExtensionResponse.result(jsonEncode(result));\n        } catch (e) {\n          return ServiceExtensionResponse.error(\n            ServiceExtensionResponse.extensionError,\n            e.toString(),\n          );\n        }\n      });\n    }\n\n    _printConnection();\n  }\n\n  static void _printConnection() {\n    Service.getInfo().then((ServiceProtocolInfo info) {\n      final serviceUri = info.serverUri;\n      if (serviceUri == null) {\n        return;\n      }\n      final port = serviceUri.port;\n      var path = serviceUri.path;\n      if (path.endsWith('/')) {\n        path = path.substring(0, path.length - 1);\n      }\n      if (path.endsWith('=')) {\n        path = path.substring(0, path.length - 1);\n      }\n      final url =\n          ' https://inspect.isar-community.dev/${Isar.version}/#/$port$path ';\n      String line(String text, String fill) {\n        final fillCount = url.length - text.length;\n        final left = List.filled(fillCount ~/ 2, fill);\n        final right = List.filled(fillCount - left.length, fill);\n        return left.join() + text + right.join();\n      }\n\n      print('╔${line('', '═')}╗');\n      print('║${line('ISAR CONNECT STARTED', ' ')}║');\n      print('╟${line('', '─')}╢');\n      print('║${line('Open the link to connect to the Isar', ' ')}║');\n      print('║${line('Inspector while this build is running.', ' ')}║');\n      print('╟${line('', '─')}╢');\n      print('║$url║');\n      print('╚${line('', '═')}╝');\n    });\n  }\n\n  static Future<dynamic> _getSchema(Map<String, dynamic> _) async {\n    return _schemas!.map((e) => e.toJson()).toList();\n  }\n\n  static Future<dynamic> _listInstances(Map<String, dynamic> _) async {\n    return Isar.instanceNames.toList();\n  }\n\n  static Future<bool> _watchInstance(Map<String, dynamic> params) async {\n    for (final sub in _collectionSubscriptions) {\n      unawaited(sub.cancel());\n    }\n\n    _collectionSubscriptions.clear();\n    if (params.isEmpty) {\n      return true;\n    }\n\n    final instanceName = params['instance'] as String;\n    final instance = Isar.getInstance(instanceName)!;\n\n    for (final collection in instance._collections.values) {\n      final sub = collection.watchLazy(fireImmediately: true).listen((_) {\n        _sendCollectionInfo(collection);\n      });\n      _collectionSubscriptions.add(sub);\n    }\n\n    return true;\n  }\n\n  static void _sendCollectionInfo(IsarCollection<dynamic> collection) {\n    final count = collection.countSync();\n    final size = collection.getSizeSync(\n      includeIndexes: true,\n      includeLinks: true,\n    );\n    final collectionInfo = ConnectCollectionInfo(\n      instance: collection.isar.name,\n      collection: collection.name,\n      size: size,\n      count: count,\n    );\n    postEvent(\n      ConnectEvent.collectionInfoChanged.event,\n      collectionInfo.toJson(),\n    );\n  }\n\n  static Future<Map<String, dynamic>> _executeQuery(\n    Map<String, dynamic> params,\n  ) async {\n    for (final sub in _querySubscription) {\n      unawaited(sub.cancel());\n    }\n    _querySubscription.clear();\n\n    final cQuery = ConnectQuery.fromJson(params);\n    final instance = Isar.getInstance(cQuery.instance)!;\n\n    final links =\n        _schemas!.firstWhere((e) => e.name == cQuery.collection).links.values;\n\n    final query = cQuery.toQuery();\n    params.remove('limit');\n    params.remove('offset');\n    final countQuery = ConnectQuery.fromJson(params).toQuery();\n\n    _querySubscription.add(\n      query.watchLazy().listen((_) {\n        postEvent(ConnectEvent.queryChanged.event, {});\n      }),\n    );\n    final subscribed = {cQuery.collection};\n    for (final link in links) {\n      if (subscribed.add(link.target)) {\n        final target = instance.getCollectionByNameInternal(link.target)!;\n        _querySubscription.add(\n          target.watchLazy().listen((_) {\n            postEvent(ConnectEvent.queryChanged.event, {});\n          }),\n        );\n      }\n    }\n\n    final objects = await query.exportJson();\n    if (links.isNotEmpty) {\n      final source = instance.getCollectionByNameInternal(cQuery.collection)!;\n      for (final object in objects) {\n        for (final link in links) {\n          final target = instance.getCollectionByNameInternal(link.target)!;\n          final links = await target.buildQuery<dynamic>(\n            whereClauses: [\n              LinkWhereClause(\n                linkCollection: source.name,\n                linkName: link.name,\n                id: object[source.schema.idName] as int,\n              ),\n            ],\n            limit: link.single ? 1 : null,\n          ).exportJson();\n\n          if (link.single) {\n            object[link.name] = links.isEmpty ? null : links.first;\n          } else {\n            object[link.name] = links;\n          }\n        }\n      }\n    }\n\n    return {\n      'objects': objects,\n      'count': await countQuery.count(),\n    };\n  }\n\n  static Future<bool> _removeQuery(Map<String, dynamic> params) async {\n    final query = ConnectQuery.fromJson(params).toQuery();\n    await query.isar.writeTxn(query.deleteAll);\n    return true;\n  }\n\n  static Future<void> _importJson(Map<String, dynamic> params) async {\n    final instance = Isar.getInstance(params['instance'] as String)!;\n    final collection =\n        instance.getCollectionByNameInternal(params['collection'] as String)!;\n    final objects = (params['objects'] as List).cast<Map<String, dynamic>>();\n    await instance.writeTxn(() async {\n      await collection.importJson(objects);\n    });\n  }\n\n  static Future<List<dynamic>> _exportJson(Map<String, dynamic> params) async {\n    final query = ConnectQuery.fromJson(params).toQuery();\n    return query.exportJson();\n  }\n\n  static Future<void> _editProperty(Map<String, dynamic> params) async {\n    final cEdit = ConnectEdit.fromJson(params);\n    final isar = Isar.getInstance(cEdit.instance)!;\n    final collection = isar.getCollectionByNameInternal(cEdit.collection)!;\n    final keys = cEdit.path.split('.');\n\n    final query = collection.buildQuery<dynamic>(\n      whereClauses: [IdWhereClause.equalTo(value: cEdit.id)],\n    );\n\n    final objects = await query.exportJson();\n    if (objects.isNotEmpty) {\n      dynamic object = objects.first;\n      for (var i = 0; i < keys.length; i++) {\n        if (i == keys.length - 1 && object is Map) {\n          object[keys[i]] = cEdit.value;\n        } else if (object is Map) {\n          object = object[keys[i]];\n        } else if (object is List) {\n          object = object[int.parse(keys[i])];\n        }\n      }\n      try {\n        await isar.writeTxn(() async {\n          await collection.importJson(objects);\n        });\n      } catch (e) {\n        print(e);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_connect_api.dart",
    "content": "// coverage:ignore-file\n// ignore_for_file: public_member_api_docs\n\nimport 'package:isar/isar.dart';\n\nenum ConnectAction {\n  getSchema('ext.isar.getSchema'),\n  listInstances('ext.isar.listInstances'),\n  watchInstance('ext.isar.watchInstance'),\n  executeQuery('ext.isar.executeQuery'),\n  removeQuery('ext.isar.removeQuery'),\n  importJson('ext.isar.importJson'),\n  exportJson('ext.isar.exportJson'),\n  editProperty('ext.isar.editProperty');\n\n  const ConnectAction(this.method);\n\n  final String method;\n}\n\nenum ConnectEvent {\n  instancesChanged('isar.instancesChanged'),\n  queryChanged('isar.queryChanged'),\n  collectionInfoChanged('isar.collectionInfoChanged');\n\n  const ConnectEvent(this.event);\n\n  final String event;\n}\n\nclass ConnectQuery {\n  ConnectQuery({\n    required this.instance,\n    required this.collection,\n    this.filter,\n    this.offset,\n    this.limit,\n    this.sortProperty,\n    this.sortAsc,\n  });\n\n  factory ConnectQuery.fromJson(Map<String, dynamic> json) {\n    return ConnectQuery(\n      instance: json['instance'] as String,\n      collection: json['collection'] as String,\n      filter: _filterFromJson(json['filter'] as Map<String, dynamic>?),\n      offset: json['offset'] as int?,\n      limit: json['limit'] as int?,\n      sortProperty: json['sortProperty'] as String?,\n      sortAsc: json['sortAsc'] as bool?,\n    );\n  }\n\n  final String instance;\n  final String collection;\n  final FilterOperation? filter;\n  final int? offset;\n  final int? limit;\n  final String? sortProperty;\n  final bool? sortAsc;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'instance': instance,\n      'collection': collection,\n      if (filter != null) 'filter': _filterToJson(filter!),\n      if (offset != null) 'offset': offset,\n      if (limit != null) 'limit': limit,\n      if (sortProperty != null) 'sortProperty': sortProperty,\n      if (sortAsc != null) 'sortAsc': sortAsc,\n    };\n  }\n\n  static FilterOperation? _filterFromJson(Map<String, dynamic>? json) {\n    if (json == null) {\n      return null;\n    }\n    if (json.containsKey('filters')) {\n      final filters = (json['filters'] as List)\n          .map((e) => _filterFromJson(e as Map<String, dynamic>?)!)\n          .toList();\n      return FilterGroup(\n        type: FilterGroupType.values[json['type'] as int],\n        filters: filters,\n      );\n    } else {\n      return FilterCondition(\n        type: FilterConditionType.values[json['type'] as int],\n        property: json['property'] as String,\n        value1: json['value1'],\n        value2: json['value2'],\n        include1: json['include1'] as bool,\n        include2: json['include2'] as bool,\n        caseSensitive: json['caseSensitive'] as bool,\n      );\n    }\n  }\n\n  static Map<String, dynamic> _filterToJson(FilterOperation filter) {\n    if (filter is FilterCondition) {\n      return {\n        'type': filter.type.index,\n        'property': filter.property,\n        'value1': filter.value1,\n        'value2': filter.value2,\n        'include1': filter.include1,\n        'include2': filter.include2,\n        'caseSensitive': filter.caseSensitive,\n      };\n    } else if (filter is FilterGroup) {\n      return {\n        'type': filter.type.index,\n        'filters': filter.filters.map(_filterToJson).toList(),\n      };\n    } else {\n      throw UnimplementedError();\n    }\n  }\n\n  Query<dynamic> toQuery() {\n    final isar = Isar.getInstance(instance)!;\n    // ignore: invalid_use_of_protected_member\n    final collection = isar.getCollectionByNameInternal(this.collection)!;\n    WhereClause? whereClause;\n    var whereSort = Sort.asc;\n\n    SortProperty? sortProperty;\n    if (this.sortProperty != null) {\n      if (this.sortProperty == collection.schema.idName) {\n        whereClause = const IdWhereClause.any();\n        whereSort = sortAsc == true ? Sort.asc : Sort.desc;\n      } else {\n        sortProperty = SortProperty(\n          property: this.sortProperty!,\n          sort: sortAsc == true ? Sort.asc : Sort.desc,\n        );\n      }\n    }\n    return collection.buildQuery(\n      whereClauses: [if (whereClause != null) whereClause],\n      whereSort: whereSort,\n      filter: filter,\n      offset: offset,\n      limit: limit,\n      sortBy: [if (sortProperty != null) sortProperty],\n    );\n  }\n}\n\nclass ConnectEdit {\n  ConnectEdit({\n    required this.instance,\n    required this.collection,\n    required this.id,\n    required this.path,\n    required this.value,\n  });\n\n  factory ConnectEdit.fromJson(Map<String, dynamic> json) {\n    return ConnectEdit(\n      instance: json['instance'] as String,\n      collection: json['collection'] as String,\n      id: json['id'] as Id,\n      path: json['path'] as String,\n      value: json['value'],\n    );\n  }\n\n  final String instance;\n  final String collection;\n  final Id id;\n  final String path;\n  final dynamic value;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'instance': instance,\n      'collection': collection,\n      'id': id,\n      'path': path,\n      'value': value,\n    };\n  }\n}\n\nclass ConnectCollectionInfo {\n  ConnectCollectionInfo({\n    required this.instance,\n    required this.collection,\n    required this.size,\n    required this.count,\n  });\n\n  factory ConnectCollectionInfo.fromJson(Map<String, dynamic> json) {\n    return ConnectCollectionInfo(\n      instance: json['instance'] as String,\n      collection: json['collection'] as String,\n      size: json['size'] as int,\n      count: json['count'] as int,\n    );\n  }\n  final String instance;\n  final String collection;\n  final int size;\n  final int count;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'instance': instance,\n      'collection': collection,\n      'size': size,\n      'count': count,\n    };\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_error.dart",
    "content": "part of isar;\n\n/// An error raised by Isar.\nclass IsarError extends Error {\n  /// @nodoc\n  @protected\n  IsarError(this.message);\n\n  /// The message\n  final String message;\n\n  @override\n  String toString() {\n    return 'IsarError: $message';\n  }\n}\n\n/// This error is returned when a unique index constraint is violated.\nclass IsarUniqueViolationError extends IsarError {\n  /// @nodoc\n  @protected\n  IsarUniqueViolationError() : super('Unique index violated');\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_link.dart",
    "content": "part of isar;\n\n/// @nodoc\n@sealed\nabstract class IsarLinkBase<OBJ> {\n  /// Is the containing object managed by Isar?\n  bool get isAttached;\n\n  /// Have the contents been changed? If not, `.save()` is a no-op.\n  bool get isChanged;\n\n  /// Has this link been loaded?\n  bool get isLoaded;\n\n  /// {@template link_load}\n  /// Loads the linked object(s) from the database\n  /// {@endtemplate}\n  Future<void> load();\n\n  /// {@macro link_load}\n  void loadSync();\n\n  /// {@template link_save}\n  /// Saves the linked object(s) to the database if there are changes.\n  ///\n  /// Also puts new objects into the database that have id set to `null` or\n  /// `Isar.autoIncrement`.\n  /// {@endtemplate}\n  Future<void> save();\n\n  /// {@macro link_save}\n  void saveSync();\n\n  /// {@template link_reset}\n  /// Unlinks all linked object(s).\n  ///\n  /// You can even call this method on links that have not been loaded yet.\n  /// {@endtemplate}\n  Future<void> reset();\n\n  /// {@macro link_reset}\n  void resetSync();\n\n  /// @nodoc\n  @protected\n  void attach(\n    IsarCollection<dynamic> sourceCollection,\n    IsarCollection<OBJ> targetCollection,\n    String linkName,\n    Id? objectId,\n  );\n}\n\n/// Establishes a 1:1 relationship with the same or another collection. The\n/// target collection is specified by the generic type argument.\nabstract class IsarLink<OBJ> implements IsarLinkBase<OBJ> {\n  /// Create an empty, unattached link. Make sure to provide the correct\n  /// generic argument.\n  factory IsarLink() => IsarLinkImpl();\n\n  /// The linked object or `null` if no object is linked.\n  OBJ? get value;\n\n  /// The linked object or `null` if no object is linked.\n  set value(OBJ? obj);\n}\n\n/// Establishes a 1:n relationship with the same or another collection. The\n/// target collection is specified by the generic type argument.\nabstract class IsarLinks<OBJ> implements IsarLinkBase<OBJ>, Set<OBJ> {\n  /// Create an empty, unattached link. Make sure to provide the correct\n  /// generic argument.\n  factory IsarLinks() => IsarLinksImpl();\n\n  @override\n  Future<void> load({bool overrideChanges = true});\n\n  @override\n  void loadSync({bool overrideChanges = true});\n\n  /// {@template links_update}\n  /// Creates and removes the specified links in the database.\n  ///\n  /// This operation does not alter the state of the local copy of this link\n  /// and it can even be used without loading the link.\n  /// {@endtemplate}\n  Future<void> update({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  });\n\n  /// {@macro links_update}\n  void updateSync({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  });\n\n  /// Starts a query for linked objects.\n  QueryBuilder<OBJ, OBJ, QAfterFilterCondition> filter();\n\n  /// {@template links_count}\n  /// Counts the linked objects in the database.\n  ///\n  /// It does not take the local state into account and can even be used\n  /// without loading the link.\n  /// {@endtemplate}\n  Future<int> count() => filter().count();\n\n  /// {@macro links_count}\n  int countSync() => filter().countSync();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_reader.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\npart of isar;\n\n/// @nodoc\n@protected\nabstract class IsarReader {\n  bool readBool(int offset);\n\n  bool? readBoolOrNull(int offset);\n\n  int readByte(int offset);\n\n  int? readByteOrNull(int offset);\n\n  int readInt(int offset);\n\n  int? readIntOrNull(int offset);\n\n  double readFloat(int offset);\n\n  double? readFloatOrNull(int offset);\n\n  int readLong(int offset);\n\n  int? readLongOrNull(int offset);\n\n  double readDouble(int offset);\n\n  double? readDoubleOrNull(int offset);\n\n  DateTime readDateTime(int offset);\n\n  DateTime? readDateTimeOrNull(int offset);\n\n  String readString(int offset);\n\n  String? readStringOrNull(int offset);\n\n  T? readObjectOrNull<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n  );\n\n  List<bool>? readBoolList(int offset);\n\n  List<bool?>? readBoolOrNullList(int offset);\n\n  List<int>? readByteList(int offset);\n\n  List<int>? readIntList(int offset);\n\n  List<int?>? readIntOrNullList(int offset);\n\n  List<double>? readFloatList(int offset);\n\n  List<double?>? readFloatOrNullList(int offset);\n\n  List<int>? readLongList(int offset);\n\n  List<int?>? readLongOrNullList(int offset);\n\n  List<double>? readDoubleList(int offset);\n\n  List<double?>? readDoubleOrNullList(int offset);\n\n  List<DateTime>? readDateTimeList(int offset);\n\n  List<DateTime?>? readDateTimeOrNullList(int offset);\n\n  List<String>? readStringList(int offset);\n\n  List<String?>? readStringOrNullList(int offset);\n\n  List<T>? readObjectList<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n    T defaultValue,\n  );\n\n  List<T?>? readObjectOrNullList<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n  );\n}\n"
  },
  {
    "path": "packages/isar/lib/src/isar_writer.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\npart of isar;\n\n/// @nodoc\n@protected\nabstract class IsarWriter {\n  void writeBool(int offset, bool? value);\n\n  void writeByte(int offset, int value);\n\n  void writeInt(int offset, int? value);\n\n  void writeFloat(int offset, double? value);\n\n  void writeLong(int offset, int? value);\n\n  void writeDouble(int offset, double? value);\n\n  void writeDateTime(int offset, DateTime? value);\n\n  void writeString(int offset, String? value);\n\n  void writeObject<T>(\n    int offset,\n    Map<Type, List<int>> allOffsets,\n    Serialize<T> serialize,\n    T? value,\n  );\n\n  void writeByteList(int offset, List<int>? values);\n\n  void writeBoolList(int offset, List<bool?>? values);\n\n  void writeIntList(int offset, List<int?>? values);\n\n  void writeFloatList(int offset, List<double?>? values);\n\n  void writeLongList(int offset, List<int?>? values);\n\n  void writeDoubleList(int offset, List<double?>? values);\n\n  void writeDateTimeList(int offset, List<DateTime?>? values);\n\n  void writeStringList(int offset, List<String?>? values);\n\n  void writeObjectList<T>(\n    int offset,\n    Map<Type, List<int>> allOffsets,\n    Serialize<T> serialize,\n    List<T?>? values,\n  );\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/bindings.dart",
    "content": "// ignore_for_file: camel_case_types, non_constant_identifier_names\n\n// AUTO GENERATED FILE, DO NOT EDIT.\n//\n// Generated by `package:ffigen`.\nimport 'dart:ffi' as ffi;\n\nclass IsarCoreBindings {\n  /// Holds the symbol lookup function.\n  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)\n      _lookup;\n\n  /// The symbols are looked up in [dynamicLibrary].\n  IsarCoreBindings(ffi.DynamicLibrary dynamicLibrary)\n      : _lookup = dynamicLibrary.lookup;\n\n  /// The symbols are looked up with [lookup].\n  IsarCoreBindings.fromLookup(\n      ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)\n          lookup)\n      : _lookup = lookup;\n\n  ffi.Pointer<ffi.Uint32> isar_find_word_boundaries(\n    ffi.Pointer<ffi.Uint8> input_bytes,\n    int length,\n    ffi.Pointer<ffi.Uint32> number_words,\n  ) {\n    return _isar_find_word_boundaries(\n      input_bytes,\n      length,\n      number_words,\n    );\n  }\n\n  late final _isar_find_word_boundariesPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<ffi.Uint32> Function(ffi.Pointer<ffi.Uint8>, ffi.Uint32,\n              ffi.Pointer<ffi.Uint32>)>>('isar_find_word_boundaries');\n  late final _isar_find_word_boundaries =\n      _isar_find_word_boundariesPtr.asFunction<\n          ffi.Pointer<ffi.Uint32> Function(\n              ffi.Pointer<ffi.Uint8>, int, ffi.Pointer<ffi.Uint32>)>();\n\n  void isar_free_word_boundaries(\n    ffi.Pointer<ffi.Uint32> boundaries,\n    int word_count,\n  ) {\n    return _isar_free_word_boundaries(\n      boundaries,\n      word_count,\n    );\n  }\n\n  late final _isar_free_word_boundariesPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.Uint32>,\n              ffi.Uint32)>>('isar_free_word_boundaries');\n  late final _isar_free_word_boundaries = _isar_free_word_boundariesPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Uint32>, int)>();\n\n  void isar_free_string(\n    ffi.Pointer<ffi.Char> string,\n  ) {\n    return _isar_free_string(\n      string,\n    );\n  }\n\n  late final _isar_free_stringPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(\n          'isar_free_string');\n  late final _isar_free_string =\n      _isar_free_stringPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();\n\n  ffi.Pointer<ffi.Char> isar_get_error(\n    int err_code,\n  ) {\n    return _isar_get_error(\n      err_code,\n    );\n  }\n\n  late final _isar_get_errorPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function(ffi.Int64)>>(\n          'isar_get_error');\n  late final _isar_get_error =\n      _isar_get_errorPtr.asFunction<ffi.Pointer<ffi.Char> Function(int)>();\n\n  void isar_free_c_object_set(\n    ffi.Pointer<CObjectSet> ros,\n  ) {\n    return _isar_free_c_object_set(\n      ros,\n    );\n  }\n\n  late final _isar_free_c_object_setPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<CObjectSet>)>>(\n          'isar_free_c_object_set');\n  late final _isar_free_c_object_set = _isar_free_c_object_setPtr\n      .asFunction<void Function(ffi.Pointer<CObjectSet>)>();\n\n  int isar_get(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<CObject> object,\n  ) {\n    return _isar_get(\n      collection,\n      txn,\n      object,\n    );\n  }\n\n  late final _isar_getPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>, ffi.Pointer<CObject>)>>('isar_get');\n  late final _isar_get = _isar_getPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<CObject>)>();\n\n  int isar_get_by_index(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int index_id,\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<CObject> object,\n  ) {\n    return _isar_get_by_index(\n      collection,\n      txn,\n      index_id,\n      key,\n      object,\n    );\n  }\n\n  late final _isar_get_by_indexPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<CIndexKey>,\n              ffi.Pointer<CObject>)>>('isar_get_by_index');\n  late final _isar_get_by_index = _isar_get_by_indexPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<CIndexKey>, ffi.Pointer<CObject>)>();\n\n  int isar_get_all(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<CObjectSet> objects,\n  ) {\n    return _isar_get_all(\n      collection,\n      txn,\n      objects,\n    );\n  }\n\n  late final _isar_get_allPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>, ffi.Pointer<CObjectSet>)>>('isar_get_all');\n  late final _isar_get_all = _isar_get_allPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<CObjectSet>)>();\n\n  int isar_get_all_by_index(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int index_id,\n    ffi.Pointer<ffi.Pointer<CIndexKey>> keys,\n    ffi.Pointer<CObjectSet> objects,\n  ) {\n    return _isar_get_all_by_index(\n      collection,\n      txn,\n      index_id,\n      keys,\n      objects,\n    );\n  }\n\n  late final _isar_get_all_by_indexPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<ffi.Pointer<CIndexKey>>,\n              ffi.Pointer<CObjectSet>)>>('isar_get_all_by_index');\n  late final _isar_get_all_by_index = _isar_get_all_by_indexPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<ffi.Pointer<CIndexKey>>, ffi.Pointer<CObjectSet>)>();\n\n  int isar_put(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<CObject> object,\n  ) {\n    return _isar_put(\n      collection,\n      txn,\n      object,\n    );\n  }\n\n  late final _isar_putPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>, ffi.Pointer<CObject>)>>('isar_put');\n  late final _isar_put = _isar_putPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<CObject>)>();\n\n  int isar_put_by_index(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int index_id,\n    ffi.Pointer<CObject> object,\n  ) {\n    return _isar_put_by_index(\n      collection,\n      txn,\n      index_id,\n      object,\n    );\n  }\n\n  late final _isar_put_by_indexPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<CObject>)>>('isar_put_by_index');\n  late final _isar_put_by_index = _isar_put_by_indexPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<CObject>)>();\n\n  int isar_put_all(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<CObjectSet> objects,\n  ) {\n    return _isar_put_all(\n      collection,\n      txn,\n      objects,\n    );\n  }\n\n  late final _isar_put_allPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>, ffi.Pointer<CObjectSet>)>>('isar_put_all');\n  late final _isar_put_all = _isar_put_allPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<CObjectSet>)>();\n\n  int isar_put_all_by_index(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int index_id,\n    ffi.Pointer<CObjectSet> objects,\n  ) {\n    return _isar_put_all_by_index(\n      collection,\n      txn,\n      index_id,\n      objects,\n    );\n  }\n\n  late final _isar_put_all_by_indexPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<CObjectSet>)>>('isar_put_all_by_index');\n  late final _isar_put_all_by_index = _isar_put_all_by_indexPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<CObjectSet>)>();\n\n  int isar_delete(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int id,\n    ffi.Pointer<ffi.Bool> deleted,\n  ) {\n    return _isar_delete(\n      collection,\n      txn,\n      id,\n      deleted,\n    );\n  }\n\n  late final _isar_deletePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Int64,\n              ffi.Pointer<ffi.Bool>)>>('isar_delete');\n  late final _isar_delete = _isar_deletePtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<ffi.Bool>)>();\n\n  int isar_delete_by_index(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int index_id,\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Bool> deleted,\n  ) {\n    return _isar_delete_by_index(\n      collection,\n      txn,\n      index_id,\n      key,\n      deleted,\n    );\n  }\n\n  late final _isar_delete_by_indexPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<CIndexKey>,\n              ffi.Pointer<ffi.Bool>)>>('isar_delete_by_index');\n  late final _isar_delete_by_index = _isar_delete_by_indexPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Bool>)>();\n\n  int isar_delete_all(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<ffi.Int64> ids,\n    int ids_length,\n    ffi.Pointer<ffi.Uint32> count,\n  ) {\n    return _isar_delete_all(\n      collection,\n      txn,\n      ids,\n      ids_length,\n      count,\n    );\n  }\n\n  late final _isar_delete_allPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Pointer<ffi.Int64>,\n              ffi.Uint32,\n              ffi.Pointer<ffi.Uint32>)>>('isar_delete_all');\n  late final _isar_delete_all = _isar_delete_allPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<ffi.Int64>, int, ffi.Pointer<ffi.Uint32>)>();\n\n  int isar_delete_all_by_index(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int index_id,\n    ffi.Pointer<ffi.Pointer<CIndexKey>> keys,\n    int keys_length,\n    ffi.Pointer<ffi.Uint32> count,\n  ) {\n    return _isar_delete_all_by_index(\n      collection,\n      txn,\n      index_id,\n      keys,\n      keys_length,\n      count,\n    );\n  }\n\n  late final _isar_delete_all_by_indexPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<ffi.Pointer<CIndexKey>>,\n              ffi.Uint32,\n              ffi.Pointer<ffi.Uint32>)>>('isar_delete_all_by_index');\n  late final _isar_delete_all_by_index =\n      _isar_delete_all_by_indexPtr.asFunction<\n          int Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              int,\n              ffi.Pointer<ffi.Pointer<CIndexKey>>,\n              int,\n              ffi.Pointer<ffi.Uint32>)>();\n\n  int isar_clear(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n  ) {\n    return _isar_clear(\n      collection,\n      txn,\n    );\n  }\n\n  late final _isar_clearPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>)>>('isar_clear');\n  late final _isar_clear = _isar_clearPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>)>();\n\n  int isar_json_import(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<ffi.Char> id_name,\n    ffi.Pointer<ffi.Uint8> json_bytes,\n    int json_length,\n  ) {\n    return _isar_json_import(\n      collection,\n      txn,\n      id_name,\n      json_bytes,\n      json_length,\n    );\n  }\n\n  late final _isar_json_importPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Uint8>,\n              ffi.Uint32)>>('isar_json_import');\n  late final _isar_json_import = _isar_json_importPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Uint8>, int)>();\n\n  int isar_count(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<ffi.Int64> count,\n  ) {\n    return _isar_count(\n      collection,\n      txn,\n      count,\n    );\n  }\n\n  late final _isar_countPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>, ffi.Pointer<ffi.Int64>)>>('isar_count');\n  late final _isar_count = _isar_countPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<ffi.Int64>)>();\n\n  int isar_get_size(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    bool include_indexes,\n    bool include_links,\n    ffi.Pointer<ffi.Int64> size,\n  ) {\n    return _isar_get_size(\n      collection,\n      txn,\n      include_indexes,\n      include_links,\n      size,\n    );\n  }\n\n  late final _isar_get_sizePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Bool,\n              ffi.Bool,\n              ffi.Pointer<ffi.Int64>)>>('isar_get_size');\n  late final _isar_get_size = _isar_get_sizePtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, bool,\n          bool, ffi.Pointer<ffi.Int64>)>();\n\n  int isar_verify(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<CObjectSet> objects,\n  ) {\n    return _isar_verify(\n      collection,\n      txn,\n      objects,\n    );\n  }\n\n  late final _isar_verifyPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>, ffi.Pointer<CObjectSet>)>>('isar_verify');\n  late final _isar_verify = _isar_verifyPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<CObjectSet>)>();\n\n  void isar_connect_dart_api(\n    DartPostCObjectFnType ptr,\n  ) {\n    return _isar_connect_dart_api(\n      ptr,\n    );\n  }\n\n  late final _isar_connect_dart_apiPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(DartPostCObjectFnType)>>(\n          'isar_connect_dart_api');\n  late final _isar_connect_dart_api = _isar_connect_dart_apiPtr\n      .asFunction<void Function(DartPostCObjectFnType)>();\n\n  void isar_filter_static(\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    bool value,\n  ) {\n    return _isar_filter_static(\n      filter,\n      value,\n    );\n  }\n\n  late final _isar_filter_staticPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Bool)>>('isar_filter_static');\n  late final _isar_filter_static = _isar_filter_staticPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Pointer<CFilter>>, bool)>();\n\n  void isar_filter_and_or_xor(\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    bool and,\n    bool exclusive,\n    ffi.Pointer<ffi.Pointer<CFilter>> conditions,\n    int length,\n  ) {\n    return _isar_filter_and_or_xor(\n      filter,\n      and,\n      exclusive,\n      conditions,\n      length,\n    );\n  }\n\n  late final _isar_filter_and_or_xorPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Bool,\n              ffi.Bool,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Uint32)>>('isar_filter_and_or_xor');\n  late final _isar_filter_and_or_xor = _isar_filter_and_or_xorPtr.asFunction<\n      void Function(ffi.Pointer<ffi.Pointer<CFilter>>, bool, bool,\n          ffi.Pointer<ffi.Pointer<CFilter>>, int)>();\n\n  void isar_filter_not(\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<CFilter> condition,\n  ) {\n    return _isar_filter_not(\n      filter,\n      condition,\n    );\n  }\n\n  late final _isar_filter_notPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<CFilter>)>>('isar_filter_not');\n  late final _isar_filter_not = _isar_filter_notPtr.asFunction<\n      void Function(ffi.Pointer<ffi.Pointer<CFilter>>, ffi.Pointer<CFilter>)>();\n\n  int isar_filter_object(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<CFilter> condition,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_object(\n      collection,\n      filter,\n      condition,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_objectPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<CFilter>,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_object');\n  late final _isar_filter_object = _isar_filter_objectPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, ffi.Pointer<CFilter>, int, int)>();\n\n  int isar_filter_link(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<CFilter> condition,\n    int link_id,\n  ) {\n    return _isar_filter_link(\n      collection,\n      filter,\n      condition,\n      link_id,\n    );\n  }\n\n  late final _isar_filter_linkPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<CFilter>,\n              ffi.Uint64)>>('isar_filter_link');\n  late final _isar_filter_link = _isar_filter_linkPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, ffi.Pointer<CFilter>, int)>();\n\n  int isar_filter_link_length(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    int lower,\n    int upper,\n    int link_id,\n  ) {\n    return _isar_filter_link_length(\n      collection,\n      filter,\n      lower,\n      upper,\n      link_id,\n    );\n  }\n\n  late final _isar_filter_link_lengthPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Uint32,\n              ffi.Uint32,\n              ffi.Uint64)>>('isar_filter_link_length');\n  late final _isar_filter_link_length = _isar_filter_link_lengthPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, int, int, int)>();\n\n  int isar_filter_list_length(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    int lower,\n    int upper,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_list_length(\n      collection,\n      filter,\n      lower,\n      upper,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_list_lengthPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Uint32,\n              ffi.Uint32,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_list_length');\n  late final _isar_filter_list_length = _isar_filter_list_lengthPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, int, int, int, int)>();\n\n  int isar_filter_null(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_null(\n      collection,\n      filter,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_nullPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_null');\n  late final _isar_filter_null = _isar_filter_nullPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, int, int)>();\n\n  void isar_filter_id(\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    int lower,\n    bool include_lower,\n    int upper,\n    bool include_upper,\n  ) {\n    return _isar_filter_id(\n      filter,\n      lower,\n      include_lower,\n      upper,\n      include_upper,\n    );\n  }\n\n  late final _isar_filter_idPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.Pointer<CFilter>>, ffi.Int64,\n              ffi.Bool, ffi.Int64, ffi.Bool)>>('isar_filter_id');\n  late final _isar_filter_id = _isar_filter_idPtr.asFunction<\n      void Function(ffi.Pointer<ffi.Pointer<CFilter>>, int, bool, int, bool)>();\n\n  int isar_filter_long(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    int lower,\n    bool include_lower,\n    int upper,\n    bool include_upper,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_long(\n      collection,\n      filter,\n      lower,\n      include_lower,\n      upper,\n      include_upper,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_longPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Int64,\n              ffi.Bool,\n              ffi.Int64,\n              ffi.Bool,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_long');\n  late final _isar_filter_long = _isar_filter_longPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, int, bool, int, bool, int, int)>();\n\n  int isar_filter_double(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    double lower,\n    double upper,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_double(\n      collection,\n      filter,\n      lower,\n      upper,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_doublePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Double,\n              ffi.Double,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_double');\n  late final _isar_filter_double = _isar_filter_doublePtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>, double, double, int, int)>();\n\n  int isar_filter_string(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<ffi.Char> lower,\n    bool include_lower,\n    ffi.Pointer<ffi.Char> upper,\n    bool include_upper,\n    bool case_sensitive,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_string(\n      collection,\n      filter,\n      lower,\n      include_lower,\n      upper,\n      include_upper,\n      case_sensitive,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_stringPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Bool,\n              ffi.Pointer<ffi.Char>,\n              ffi.Bool,\n              ffi.Bool,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_string');\n  late final _isar_filter_string = _isar_filter_stringPtr.asFunction<\n      int Function(\n          ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<ffi.Pointer<CFilter>>,\n          ffi.Pointer<ffi.Char>,\n          bool,\n          ffi.Pointer<ffi.Char>,\n          bool,\n          bool,\n          int,\n          int)>();\n\n  int isar_filter_string_starts_with(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<ffi.Char> value,\n    bool case_sensitive,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_string_starts_with(\n      collection,\n      filter,\n      value,\n      case_sensitive,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_string_starts_withPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Bool,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_string_starts_with');\n  late final _isar_filter_string_starts_with =\n      _isar_filter_string_starts_withPtr.asFunction<\n          int Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              bool,\n              int,\n              int)>();\n\n  int isar_filter_string_ends_with(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<ffi.Char> value,\n    bool case_sensitive,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_string_ends_with(\n      collection,\n      filter,\n      value,\n      case_sensitive,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_string_ends_withPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Bool,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_string_ends_with');\n  late final _isar_filter_string_ends_with =\n      _isar_filter_string_ends_withPtr.asFunction<\n          int Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              bool,\n              int,\n              int)>();\n\n  int isar_filter_string_contains(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<ffi.Char> value,\n    bool case_sensitive,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_string_contains(\n      collection,\n      filter,\n      value,\n      case_sensitive,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_string_containsPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Bool,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_string_contains');\n  late final _isar_filter_string_contains =\n      _isar_filter_string_containsPtr.asFunction<\n          int Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              bool,\n              int,\n              int)>();\n\n  int isar_filter_string_matches(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<ffi.Pointer<CFilter>> filter,\n    ffi.Pointer<ffi.Char> value,\n    bool case_sensitive,\n    int embedded_col_id,\n    int property_id,\n  ) {\n    return _isar_filter_string_matches(\n      collection,\n      filter,\n      value,\n      case_sensitive,\n      embedded_col_id,\n      property_id,\n    );\n  }\n\n  late final _isar_filter_string_matchesPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Bool,\n              ffi.Uint64,\n              ffi.Uint64)>>('isar_filter_string_matches');\n  late final _isar_filter_string_matches =\n      _isar_filter_string_matchesPtr.asFunction<\n          int Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<ffi.Pointer<CFilter>>,\n              ffi.Pointer<ffi.Char>,\n              bool,\n              int,\n              int)>();\n\n  void isar_key_create(\n    ffi.Pointer<ffi.Pointer<CIndexKey>> key,\n  ) {\n    return _isar_key_create(\n      key,\n    );\n  }\n\n  late final _isar_key_createPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<ffi.Pointer<CIndexKey>>)>>('isar_key_create');\n  late final _isar_key_create = _isar_key_createPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Pointer<CIndexKey>>)>();\n\n  bool isar_key_increase(\n    ffi.Pointer<CIndexKey> key,\n  ) {\n    return _isar_key_increase(\n      key,\n    );\n  }\n\n  late final _isar_key_increasePtr =\n      _lookup<ffi.NativeFunction<ffi.Bool Function(ffi.Pointer<CIndexKey>)>>(\n          'isar_key_increase');\n  late final _isar_key_increase =\n      _isar_key_increasePtr.asFunction<bool Function(ffi.Pointer<CIndexKey>)>();\n\n  bool isar_key_decrease(\n    ffi.Pointer<CIndexKey> key,\n  ) {\n    return _isar_key_decrease(\n      key,\n    );\n  }\n\n  late final _isar_key_decreasePtr =\n      _lookup<ffi.NativeFunction<ffi.Bool Function(ffi.Pointer<CIndexKey>)>>(\n          'isar_key_decrease');\n  late final _isar_key_decrease =\n      _isar_key_decreasePtr.asFunction<bool Function(ffi.Pointer<CIndexKey>)>();\n\n  void isar_key_add_byte(\n    ffi.Pointer<CIndexKey> key,\n    int value,\n  ) {\n    return _isar_key_add_byte(\n      key,\n      value,\n    );\n  }\n\n  late final _isar_key_add_bytePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<CIndexKey>, ffi.Uint8)>>('isar_key_add_byte');\n  late final _isar_key_add_byte = _isar_key_add_bytePtr\n      .asFunction<void Function(ffi.Pointer<CIndexKey>, int)>();\n\n  void isar_key_add_int(\n    ffi.Pointer<CIndexKey> key,\n    int value,\n  ) {\n    return _isar_key_add_int(\n      key,\n      value,\n    );\n  }\n\n  late final _isar_key_add_intPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<CIndexKey>, ffi.Int32)>>('isar_key_add_int');\n  late final _isar_key_add_int = _isar_key_add_intPtr\n      .asFunction<void Function(ffi.Pointer<CIndexKey>, int)>();\n\n  void isar_key_add_long(\n    ffi.Pointer<CIndexKey> key,\n    int value,\n  ) {\n    return _isar_key_add_long(\n      key,\n      value,\n    );\n  }\n\n  late final _isar_key_add_longPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<CIndexKey>, ffi.Int64)>>('isar_key_add_long');\n  late final _isar_key_add_long = _isar_key_add_longPtr\n      .asFunction<void Function(ffi.Pointer<CIndexKey>, int)>();\n\n  void isar_key_add_float(\n    ffi.Pointer<CIndexKey> key,\n    double value,\n  ) {\n    return _isar_key_add_float(\n      key,\n      value,\n    );\n  }\n\n  late final _isar_key_add_floatPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<CIndexKey>, ffi.Double)>>('isar_key_add_float');\n  late final _isar_key_add_float = _isar_key_add_floatPtr\n      .asFunction<void Function(ffi.Pointer<CIndexKey>, double)>();\n\n  void isar_key_add_double(\n    ffi.Pointer<CIndexKey> key,\n    double value,\n  ) {\n    return _isar_key_add_double(\n      key,\n      value,\n    );\n  }\n\n  late final _isar_key_add_doublePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<CIndexKey>, ffi.Double)>>('isar_key_add_double');\n  late final _isar_key_add_double = _isar_key_add_doublePtr\n      .asFunction<void Function(ffi.Pointer<CIndexKey>, double)>();\n\n  void isar_key_add_string(\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Char> value,\n    bool case_sensitive,\n  ) {\n    return _isar_key_add_string(\n      key,\n      value,\n      case_sensitive,\n    );\n  }\n\n  late final _isar_key_add_stringPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Char>,\n              ffi.Bool)>>('isar_key_add_string');\n  late final _isar_key_add_string = _isar_key_add_stringPtr.asFunction<\n      void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Char>, bool)>();\n\n  void isar_key_add_string_hash(\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Char> value,\n    bool case_sensitive,\n  ) {\n    return _isar_key_add_string_hash(\n      key,\n      value,\n      case_sensitive,\n    );\n  }\n\n  late final _isar_key_add_string_hashPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Char>,\n              ffi.Bool)>>('isar_key_add_string_hash');\n  late final _isar_key_add_string_hash =\n      _isar_key_add_string_hashPtr.asFunction<\n          void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Char>, bool)>();\n\n  void isar_key_add_string_list_hash(\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> value,\n    int length,\n    bool case_sensitive,\n  ) {\n    return _isar_key_add_string_list_hash(\n      key,\n      value,\n      length,\n      case_sensitive,\n    );\n  }\n\n  late final _isar_key_add_string_list_hashPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<CIndexKey>,\n              ffi.Pointer<ffi.Pointer<ffi.Char>>,\n              ffi.Uint32,\n              ffi.Bool)>>('isar_key_add_string_list_hash');\n  late final _isar_key_add_string_list_hash =\n      _isar_key_add_string_list_hashPtr.asFunction<\n          void Function(ffi.Pointer<CIndexKey>,\n              ffi.Pointer<ffi.Pointer<ffi.Char>>, int, bool)>();\n\n  void isar_key_add_byte_list_hash(\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Uint8> value,\n    int length,\n  ) {\n    return _isar_key_add_byte_list_hash(\n      key,\n      value,\n      length,\n    );\n  }\n\n  late final _isar_key_add_byte_list_hashPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Uint8>,\n              ffi.Uint32)>>('isar_key_add_byte_list_hash');\n  late final _isar_key_add_byte_list_hash =\n      _isar_key_add_byte_list_hashPtr.asFunction<\n          void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Uint8>, int)>();\n\n  void isar_key_add_int_list_hash(\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Int32> value,\n    int length,\n  ) {\n    return _isar_key_add_int_list_hash(\n      key,\n      value,\n      length,\n    );\n  }\n\n  late final _isar_key_add_int_list_hashPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Int32>,\n              ffi.Uint32)>>('isar_key_add_int_list_hash');\n  late final _isar_key_add_int_list_hash =\n      _isar_key_add_int_list_hashPtr.asFunction<\n          void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Int32>, int)>();\n\n  void isar_key_add_long_list_hash(\n    ffi.Pointer<CIndexKey> key,\n    ffi.Pointer<ffi.Int64> value,\n    int length,\n  ) {\n    return _isar_key_add_long_list_hash(\n      key,\n      value,\n      length,\n    );\n  }\n\n  late final _isar_key_add_long_list_hashPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Int64>,\n              ffi.Uint32)>>('isar_key_add_long_list_hash');\n  late final _isar_key_add_long_list_hash =\n      _isar_key_add_long_list_hashPtr.asFunction<\n          void Function(ffi.Pointer<CIndexKey>, ffi.Pointer<ffi.Int64>, int)>();\n\n  ffi.Pointer<ffi.Char> isar_version() {\n    return _isar_version();\n  }\n\n  late final _isar_versionPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n          'isar_version');\n  late final _isar_version =\n      _isar_versionPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  int isar_instance_create(\n    ffi.Pointer<ffi.Pointer<CIsarInstance>> isar,\n    ffi.Pointer<ffi.Char> name,\n    ffi.Pointer<ffi.Char> path,\n    ffi.Pointer<ffi.Char> schema_json,\n    int max_size_mib,\n    bool relaxed_durability,\n    int compact_min_file_size,\n    int compact_min_bytes,\n    double compact_min_ratio,\n  ) {\n    return _isar_instance_create(\n      isar,\n      name,\n      path,\n      schema_json,\n      max_size_mib,\n      relaxed_durability,\n      compact_min_file_size,\n      compact_min_bytes,\n      compact_min_ratio,\n    );\n  }\n\n  late final _isar_instance_createPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<ffi.Pointer<CIsarInstance>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Int64,\n              ffi.Bool,\n              ffi.Uint32,\n              ffi.Uint32,\n              ffi.Double)>>('isar_instance_create');\n  late final _isar_instance_create = _isar_instance_createPtr.asFunction<\n      int Function(\n          ffi.Pointer<ffi.Pointer<CIsarInstance>>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          int,\n          bool,\n          int,\n          int,\n          double)>();\n\n  void isar_instance_create_async(\n    ffi.Pointer<ffi.Pointer<CIsarInstance>> isar,\n    ffi.Pointer<ffi.Char> name,\n    ffi.Pointer<ffi.Char> path,\n    ffi.Pointer<ffi.Char> schema_json,\n    int max_size_mib,\n    bool relaxed_durability,\n    int compact_min_file_size,\n    int compact_min_bytes,\n    double compact_min_ratio,\n    int port,\n  ) {\n    return _isar_instance_create_async(\n      isar,\n      name,\n      path,\n      schema_json,\n      max_size_mib,\n      relaxed_durability,\n      compact_min_file_size,\n      compact_min_bytes,\n      compact_min_ratio,\n      port,\n    );\n  }\n\n  late final _isar_instance_create_asyncPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<ffi.Pointer<CIsarInstance>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Int64,\n              ffi.Bool,\n              ffi.Uint32,\n              ffi.Uint32,\n              ffi.Double,\n              DartPort)>>('isar_instance_create_async');\n  late final _isar_instance_create_async =\n      _isar_instance_create_asyncPtr.asFunction<\n          void Function(\n              ffi.Pointer<ffi.Pointer<CIsarInstance>>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Char>,\n              int,\n              bool,\n              int,\n              int,\n              double,\n              int)>();\n\n  bool isar_instance_close(\n    ffi.Pointer<CIsarInstance> isar,\n  ) {\n    return _isar_instance_close(\n      isar,\n    );\n  }\n\n  late final _isar_instance_closePtr = _lookup<\n          ffi.NativeFunction<ffi.Bool Function(ffi.Pointer<CIsarInstance>)>>(\n      'isar_instance_close');\n  late final _isar_instance_close = _isar_instance_closePtr\n      .asFunction<bool Function(ffi.Pointer<CIsarInstance>)>();\n\n  bool isar_instance_close_and_delete(\n    ffi.Pointer<CIsarInstance> isar,\n  ) {\n    return _isar_instance_close_and_delete(\n      isar,\n    );\n  }\n\n  late final _isar_instance_close_and_deletePtr = _lookup<\n          ffi.NativeFunction<ffi.Bool Function(ffi.Pointer<CIsarInstance>)>>(\n      'isar_instance_close_and_delete');\n  late final _isar_instance_close_and_delete =\n      _isar_instance_close_and_deletePtr\n          .asFunction<bool Function(ffi.Pointer<CIsarInstance>)>();\n\n  ffi.Pointer<ffi.Char> isar_instance_get_path(\n    ffi.Pointer<CIsarInstance> isar,\n  ) {\n    return _isar_instance_get_path(\n      isar,\n    );\n  }\n\n  late final _isar_instance_get_pathPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n              ffi.Pointer<CIsarInstance>)>>('isar_instance_get_path');\n  late final _isar_instance_get_path = _isar_instance_get_pathPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<CIsarInstance>)>();\n\n  int isar_instance_get_collection(\n    ffi.Pointer<CIsarInstance> isar,\n    ffi.Pointer<ffi.Pointer<CIsarCollection>> collection,\n    int collection_id,\n  ) {\n    return _isar_instance_get_collection(\n      isar,\n      collection,\n      collection_id,\n    );\n  }\n\n  late final _isar_instance_get_collectionPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<ffi.Pointer<CIsarCollection>>,\n              ffi.Uint64)>>('isar_instance_get_collection');\n  late final _isar_instance_get_collection =\n      _isar_instance_get_collectionPtr.asFunction<\n          int Function(ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<ffi.Pointer<CIsarCollection>>, int)>();\n\n  int isar_instance_get_size(\n    ffi.Pointer<CIsarInstance> instance,\n    ffi.Pointer<CIsarTxn> txn,\n    bool include_indexes,\n    bool include_links,\n    ffi.Pointer<ffi.Int64> size,\n  ) {\n    return _isar_instance_get_size(\n      instance,\n      txn,\n      include_indexes,\n      include_links,\n      size,\n    );\n  }\n\n  late final _isar_instance_get_sizePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Bool,\n              ffi.Bool,\n              ffi.Pointer<ffi.Int64>)>>('isar_instance_get_size');\n  late final _isar_instance_get_size = _isar_instance_get_sizePtr.asFunction<\n      int Function(ffi.Pointer<CIsarInstance>, ffi.Pointer<CIsarTxn>, bool,\n          bool, ffi.Pointer<ffi.Int64>)>();\n\n  void isar_instance_copy_to_file(\n    ffi.Pointer<CIsarInstance> instance,\n    ffi.Pointer<ffi.Char> path,\n    int port,\n  ) {\n    return _isar_instance_copy_to_file(\n      instance,\n      path,\n      port,\n    );\n  }\n\n  late final _isar_instance_copy_to_filePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CIsarInstance>, ffi.Pointer<ffi.Char>,\n              DartPort)>>('isar_instance_copy_to_file');\n  late final _isar_instance_copy_to_file =\n      _isar_instance_copy_to_filePtr.asFunction<\n          void Function(\n              ffi.Pointer<CIsarInstance>, ffi.Pointer<ffi.Char>, int)>();\n\n  int isar_instance_verify(\n    ffi.Pointer<CIsarInstance> instance,\n    ffi.Pointer<CIsarTxn> txn,\n  ) {\n    return _isar_instance_verify(\n      instance,\n      txn,\n    );\n  }\n\n  late final _isar_instance_verifyPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<CIsarTxn>)>>('isar_instance_verify');\n  late final _isar_instance_verify = _isar_instance_verifyPtr.asFunction<\n      int Function(ffi.Pointer<CIsarInstance>, ffi.Pointer<CIsarTxn>)>();\n\n  int isar_get_offsets(\n    ffi.Pointer<CIsarCollection> collection,\n    int embedded_col_id,\n    ffi.Pointer<ffi.Uint32> offsets,\n  ) {\n    return _isar_get_offsets(\n      collection,\n      embedded_col_id,\n      offsets,\n    );\n  }\n\n  late final _isar_get_offsetsPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Uint32 Function(ffi.Pointer<CIsarCollection>, ffi.Uint64,\n              ffi.Pointer<ffi.Uint32>)>>('isar_get_offsets');\n  late final _isar_get_offsets = _isar_get_offsetsPtr.asFunction<\n      int Function(\n          ffi.Pointer<CIsarCollection>, int, ffi.Pointer<ffi.Uint32>)>();\n\n  int isar_link(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int link_id,\n    int id,\n    int target_id,\n  ) {\n    return _isar_link(\n      collection,\n      txn,\n      link_id,\n      id,\n      target_id,\n    );\n  }\n\n  late final _isar_linkPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Int64,\n              ffi.Int64)>>('isar_link');\n  late final _isar_link = _isar_linkPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          int, int)>();\n\n  int isar_link_unlink(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int link_id,\n    int id,\n    int target_id,\n  ) {\n    return _isar_link_unlink(\n      collection,\n      txn,\n      link_id,\n      id,\n      target_id,\n    );\n  }\n\n  late final _isar_link_unlinkPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Int64,\n              ffi.Int64)>>('isar_link_unlink');\n  late final _isar_link_unlink = _isar_link_unlinkPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          int, int)>();\n\n  int isar_link_unlink_all(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int link_id,\n    int id,\n  ) {\n    return _isar_link_unlink_all(\n      collection,\n      txn,\n      link_id,\n      id,\n    );\n  }\n\n  late final _isar_link_unlink_allPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Int64)>>('isar_link_unlink_all');\n  late final _isar_link_unlink_all = _isar_link_unlink_allPtr.asFunction<\n      int Function(\n          ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int, int)>();\n\n  int isar_link_update_all(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int link_id,\n    int id,\n    ffi.Pointer<ffi.Int64> ids,\n    int link_count,\n    int unlink_count,\n    bool replace,\n  ) {\n    return _isar_link_update_all(\n      collection,\n      txn,\n      link_id,\n      id,\n      ids,\n      link_count,\n      unlink_count,\n      replace,\n    );\n  }\n\n  late final _isar_link_update_allPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Int64,\n              ffi.Pointer<ffi.Int64>,\n              ffi.Uint32,\n              ffi.Uint32,\n              ffi.Bool)>>('isar_link_update_all');\n  late final _isar_link_update_all = _isar_link_update_allPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          int, ffi.Pointer<ffi.Int64>, int, int, bool)>();\n\n  int isar_link_verify(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int link_id,\n    ffi.Pointer<ffi.Int64> ids,\n    int ids_count,\n  ) {\n    return _isar_link_verify(\n      collection,\n      txn,\n      link_id,\n      ids,\n      ids_count,\n    );\n  }\n\n  late final _isar_link_verifyPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint64,\n              ffi.Pointer<ffi.Int64>,\n              ffi.Uint32)>>('isar_link_verify');\n  late final _isar_link_verify = _isar_link_verifyPtr.asFunction<\n      int Function(ffi.Pointer<CIsarCollection>, ffi.Pointer<CIsarTxn>, int,\n          ffi.Pointer<ffi.Int64>, int)>();\n\n  ffi.Pointer<CQueryBuilder> isar_qb_create(\n    ffi.Pointer<CIsarCollection> collection,\n  ) {\n    return _isar_qb_create(\n      collection,\n    );\n  }\n\n  late final _isar_qb_createPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<CQueryBuilder> Function(\n              ffi.Pointer<CIsarCollection>)>>('isar_qb_create');\n  late final _isar_qb_create = _isar_qb_createPtr.asFunction<\n      ffi.Pointer<CQueryBuilder> Function(ffi.Pointer<CIsarCollection>)>();\n\n  int isar_qb_add_id_where_clause(\n    ffi.Pointer<CQueryBuilder> builder,\n    int start_id,\n    int end_id,\n  ) {\n    return _isar_qb_add_id_where_clause(\n      builder,\n      start_id,\n      end_id,\n    );\n  }\n\n  late final _isar_qb_add_id_where_clausePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CQueryBuilder>, ffi.Int64,\n              ffi.Int64)>>('isar_qb_add_id_where_clause');\n  late final _isar_qb_add_id_where_clause = _isar_qb_add_id_where_clausePtr\n      .asFunction<int Function(ffi.Pointer<CQueryBuilder>, int, int)>();\n\n  int isar_qb_add_index_where_clause(\n    ffi.Pointer<CQueryBuilder> builder,\n    int index_id,\n    ffi.Pointer<CIndexKey> lower_key,\n    ffi.Pointer<CIndexKey> upper_key,\n    bool sort_asc,\n    bool skip_duplicates,\n  ) {\n    return _isar_qb_add_index_where_clause(\n      builder,\n      index_id,\n      lower_key,\n      upper_key,\n      sort_asc,\n      skip_duplicates,\n    );\n  }\n\n  late final _isar_qb_add_index_where_clausePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CQueryBuilder>,\n              ffi.Uint64,\n              ffi.Pointer<CIndexKey>,\n              ffi.Pointer<CIndexKey>,\n              ffi.Bool,\n              ffi.Bool)>>('isar_qb_add_index_where_clause');\n  late final _isar_qb_add_index_where_clause =\n      _isar_qb_add_index_where_clausePtr.asFunction<\n          int Function(ffi.Pointer<CQueryBuilder>, int, ffi.Pointer<CIndexKey>,\n              ffi.Pointer<CIndexKey>, bool, bool)>();\n\n  int isar_qb_add_link_where_clause(\n    ffi.Pointer<CQueryBuilder> builder,\n    ffi.Pointer<CIsarCollection> source_collection,\n    int link_id,\n    int id,\n  ) {\n    return _isar_qb_add_link_where_clause(\n      builder,\n      source_collection,\n      link_id,\n      id,\n    );\n  }\n\n  late final _isar_qb_add_link_where_clausePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CQueryBuilder>,\n              ffi.Pointer<CIsarCollection>,\n              ffi.Uint64,\n              ffi.Int64)>>('isar_qb_add_link_where_clause');\n  late final _isar_qb_add_link_where_clause =\n      _isar_qb_add_link_where_clausePtr.asFunction<\n          int Function(ffi.Pointer<CQueryBuilder>, ffi.Pointer<CIsarCollection>,\n              int, int)>();\n\n  void isar_qb_set_filter(\n    ffi.Pointer<CQueryBuilder> builder,\n    ffi.Pointer<CFilter> filter,\n  ) {\n    return _isar_qb_set_filter(\n      builder,\n      filter,\n    );\n  }\n\n  late final _isar_qb_set_filterPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CQueryBuilder>,\n              ffi.Pointer<CFilter>)>>('isar_qb_set_filter');\n  late final _isar_qb_set_filter = _isar_qb_set_filterPtr.asFunction<\n      void Function(ffi.Pointer<CQueryBuilder>, ffi.Pointer<CFilter>)>();\n\n  int isar_qb_add_sort_by(\n    ffi.Pointer<CQueryBuilder> builder,\n    int property_id,\n    bool asc,\n  ) {\n    return _isar_qb_add_sort_by(\n      builder,\n      property_id,\n      asc,\n    );\n  }\n\n  late final _isar_qb_add_sort_byPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CQueryBuilder>, ffi.Uint64,\n              ffi.Bool)>>('isar_qb_add_sort_by');\n  late final _isar_qb_add_sort_by = _isar_qb_add_sort_byPtr\n      .asFunction<int Function(ffi.Pointer<CQueryBuilder>, int, bool)>();\n\n  int isar_qb_add_distinct_by(\n    ffi.Pointer<CQueryBuilder> builder,\n    int property_id,\n    bool case_sensitive,\n  ) {\n    return _isar_qb_add_distinct_by(\n      builder,\n      property_id,\n      case_sensitive,\n    );\n  }\n\n  late final _isar_qb_add_distinct_byPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CQueryBuilder>, ffi.Uint64,\n              ffi.Bool)>>('isar_qb_add_distinct_by');\n  late final _isar_qb_add_distinct_by = _isar_qb_add_distinct_byPtr\n      .asFunction<int Function(ffi.Pointer<CQueryBuilder>, int, bool)>();\n\n  void isar_qb_set_offset_limit(\n    ffi.Pointer<CQueryBuilder> builder,\n    int offset,\n    int limit,\n  ) {\n    return _isar_qb_set_offset_limit(\n      builder,\n      offset,\n      limit,\n    );\n  }\n\n  late final _isar_qb_set_offset_limitPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<CQueryBuilder>, ffi.Int64,\n              ffi.Int64)>>('isar_qb_set_offset_limit');\n  late final _isar_qb_set_offset_limit = _isar_qb_set_offset_limitPtr\n      .asFunction<void Function(ffi.Pointer<CQueryBuilder>, int, int)>();\n\n  ffi.Pointer<CQuery> isar_qb_build(\n    ffi.Pointer<CQueryBuilder> builder,\n  ) {\n    return _isar_qb_build(\n      builder,\n    );\n  }\n\n  late final _isar_qb_buildPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<CQuery> Function(\n              ffi.Pointer<CQueryBuilder>)>>('isar_qb_build');\n  late final _isar_qb_build = _isar_qb_buildPtr\n      .asFunction<ffi.Pointer<CQuery> Function(ffi.Pointer<CQueryBuilder>)>();\n\n  void isar_q_free(\n    ffi.Pointer<CQuery> query,\n  ) {\n    return _isar_q_free(\n      query,\n    );\n  }\n\n  late final _isar_q_freePtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<CQuery>)>>(\n          'isar_q_free');\n  late final _isar_q_free =\n      _isar_q_freePtr.asFunction<void Function(ffi.Pointer<CQuery>)>();\n\n  int isar_q_find(\n    ffi.Pointer<CQuery> query,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<CObjectSet> result,\n    int limit,\n  ) {\n    return _isar_q_find(\n      query,\n      txn,\n      result,\n      limit,\n    );\n  }\n\n  late final _isar_q_findPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(ffi.Pointer<CQuery>, ffi.Pointer<CIsarTxn>,\n              ffi.Pointer<CObjectSet>, ffi.Uint32)>>('isar_q_find');\n  late final _isar_q_find = _isar_q_findPtr.asFunction<\n      int Function(ffi.Pointer<CQuery>, ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<CObjectSet>, int)>();\n\n  int isar_q_delete(\n    ffi.Pointer<CQuery> query,\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    int limit,\n    ffi.Pointer<ffi.Uint32> count,\n  ) {\n    return _isar_q_delete(\n      query,\n      collection,\n      txn,\n      limit,\n      count,\n    );\n  }\n\n  late final _isar_q_deletePtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CQuery>,\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Uint32,\n              ffi.Pointer<ffi.Uint32>)>>('isar_q_delete');\n  late final _isar_q_delete = _isar_q_deletePtr.asFunction<\n      int Function(ffi.Pointer<CQuery>, ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<CIsarTxn>, int, ffi.Pointer<ffi.Uint32>)>();\n\n  int isar_q_export_json(\n    ffi.Pointer<CQuery> query,\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CIsarTxn> txn,\n    ffi.Pointer<ffi.Char> id_name,\n    ffi.Pointer<ffi.Pointer<ffi.Uint8>> json_bytes,\n    ffi.Pointer<ffi.Uint32> json_length,\n  ) {\n    return _isar_q_export_json(\n      query,\n      collection,\n      txn,\n      id_name,\n      json_bytes,\n      json_length,\n    );\n  }\n\n  late final _isar_q_export_jsonPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CQuery>,\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CIsarTxn>,\n              ffi.Pointer<ffi.Char>,\n              ffi.Pointer<ffi.Pointer<ffi.Uint8>>,\n              ffi.Pointer<ffi.Uint32>)>>('isar_q_export_json');\n  late final _isar_q_export_json = _isar_q_export_jsonPtr.asFunction<\n      int Function(\n          ffi.Pointer<CQuery>,\n          ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<CIsarTxn>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Uint8>>,\n          ffi.Pointer<ffi.Uint32>)>();\n\n  void isar_free_json(\n    ffi.Pointer<ffi.Uint8> json_bytes,\n    int json_length,\n  ) {\n    return _isar_free_json(\n      json_bytes,\n      json_length,\n    );\n  }\n\n  late final _isar_free_jsonPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Void Function(\n              ffi.Pointer<ffi.Uint8>, ffi.Uint32)>>('isar_free_json');\n  late final _isar_free_json = _isar_free_jsonPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Uint8>, int)>();\n\n  int isar_q_aggregate(\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CQuery> query,\n    ffi.Pointer<CIsarTxn> txn,\n    int operation,\n    int property_id,\n    ffi.Pointer<ffi.Pointer<CAggregationResult>> result,\n  ) {\n    return _isar_q_aggregate(\n      collection,\n      query,\n      txn,\n      operation,\n      property_id,\n      result,\n    );\n  }\n\n  late final _isar_q_aggregatePtr = _lookup<\n          ffi.NativeFunction<\n              ffi.Int64 Function(\n                  ffi.Pointer<CIsarCollection>,\n                  ffi.Pointer<CQuery>,\n                  ffi.Pointer<CIsarTxn>,\n                  ffi.Uint8,\n                  ffi.Uint64,\n                  ffi.Pointer<ffi.Pointer<CAggregationResult>>)>>(\n      'isar_q_aggregate');\n  late final _isar_q_aggregate = _isar_q_aggregatePtr.asFunction<\n      int Function(\n          ffi.Pointer<CIsarCollection>,\n          ffi.Pointer<CQuery>,\n          ffi.Pointer<CIsarTxn>,\n          int,\n          int,\n          ffi.Pointer<ffi.Pointer<CAggregationResult>>)>();\n\n  int isar_q_aggregate_long_result(\n    ffi.Pointer<CAggregationResult> result,\n  ) {\n    return _isar_q_aggregate_long_result(\n      result,\n    );\n  }\n\n  late final _isar_q_aggregate_long_resultPtr = _lookup<\n          ffi.NativeFunction<\n              ffi.Int64 Function(ffi.Pointer<CAggregationResult>)>>(\n      'isar_q_aggregate_long_result');\n  late final _isar_q_aggregate_long_result = _isar_q_aggregate_long_resultPtr\n      .asFunction<int Function(ffi.Pointer<CAggregationResult>)>();\n\n  double isar_q_aggregate_double_result(\n    ffi.Pointer<CAggregationResult> result,\n  ) {\n    return _isar_q_aggregate_double_result(\n      result,\n    );\n  }\n\n  late final _isar_q_aggregate_double_resultPtr = _lookup<\n          ffi.NativeFunction<\n              ffi.Double Function(ffi.Pointer<CAggregationResult>)>>(\n      'isar_q_aggregate_double_result');\n  late final _isar_q_aggregate_double_result =\n      _isar_q_aggregate_double_resultPtr\n          .asFunction<double Function(ffi.Pointer<CAggregationResult>)>();\n\n  int isar_txn_begin(\n    ffi.Pointer<CIsarInstance> isar,\n    ffi.Pointer<ffi.Pointer<CIsarTxn>> txn,\n    bool sync1,\n    bool write,\n    bool silent,\n    int port,\n  ) {\n    return _isar_txn_begin(\n      isar,\n      txn,\n      sync1,\n      write,\n      silent,\n      port,\n    );\n  }\n\n  late final _isar_txn_beginPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<ffi.Pointer<CIsarTxn>>,\n              ffi.Bool,\n              ffi.Bool,\n              ffi.Bool,\n              DartPort)>>('isar_txn_begin');\n  late final _isar_txn_begin = _isar_txn_beginPtr.asFunction<\n      int Function(ffi.Pointer<CIsarInstance>,\n          ffi.Pointer<ffi.Pointer<CIsarTxn>>, bool, bool, bool, int)>();\n\n  int isar_txn_finish(\n    ffi.Pointer<CIsarTxn> txn,\n    bool commit,\n  ) {\n    return _isar_txn_finish(\n      txn,\n      commit,\n    );\n  }\n\n  late final _isar_txn_finishPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Int64 Function(\n              ffi.Pointer<CIsarTxn>, ffi.Bool)>>('isar_txn_finish');\n  late final _isar_txn_finish = _isar_txn_finishPtr\n      .asFunction<int Function(ffi.Pointer<CIsarTxn>, bool)>();\n\n  ffi.Pointer<CWatchHandle> isar_watch_collection(\n    ffi.Pointer<CIsarInstance> isar,\n    ffi.Pointer<CIsarCollection> collection,\n    int port,\n  ) {\n    return _isar_watch_collection(\n      isar,\n      collection,\n      port,\n    );\n  }\n\n  late final _isar_watch_collectionPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<CWatchHandle> Function(\n              ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<CIsarCollection>,\n              DartPort)>>('isar_watch_collection');\n  late final _isar_watch_collection = _isar_watch_collectionPtr.asFunction<\n      ffi.Pointer<CWatchHandle> Function(\n          ffi.Pointer<CIsarInstance>, ffi.Pointer<CIsarCollection>, int)>();\n\n  ffi.Pointer<CWatchHandle> isar_watch_object(\n    ffi.Pointer<CIsarInstance> isar,\n    ffi.Pointer<CIsarCollection> collection,\n    int id,\n    int port,\n  ) {\n    return _isar_watch_object(\n      isar,\n      collection,\n      id,\n      port,\n    );\n  }\n\n  late final _isar_watch_objectPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<CWatchHandle> Function(\n              ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<CIsarCollection>,\n              ffi.Int64,\n              DartPort)>>('isar_watch_object');\n  late final _isar_watch_object = _isar_watch_objectPtr.asFunction<\n      ffi.Pointer<CWatchHandle> Function(ffi.Pointer<CIsarInstance>,\n          ffi.Pointer<CIsarCollection>, int, int)>();\n\n  ffi.Pointer<CWatchHandle> isar_watch_query(\n    ffi.Pointer<CIsarInstance> isar,\n    ffi.Pointer<CIsarCollection> collection,\n    ffi.Pointer<CQuery> query,\n    int port,\n  ) {\n    return _isar_watch_query(\n      isar,\n      collection,\n      query,\n      port,\n    );\n  }\n\n  late final _isar_watch_queryPtr = _lookup<\n      ffi.NativeFunction<\n          ffi.Pointer<CWatchHandle> Function(\n              ffi.Pointer<CIsarInstance>,\n              ffi.Pointer<CIsarCollection>,\n              ffi.Pointer<CQuery>,\n              DartPort)>>('isar_watch_query');\n  late final _isar_watch_query = _isar_watch_queryPtr.asFunction<\n      ffi.Pointer<CWatchHandle> Function(ffi.Pointer<CIsarInstance>,\n          ffi.Pointer<CIsarCollection>, ffi.Pointer<CQuery>, int)>();\n\n  void isar_stop_watching(\n    ffi.Pointer<CWatchHandle> handle,\n  ) {\n    return _isar_stop_watching(\n      handle,\n    );\n  }\n\n  late final _isar_stop_watchingPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<CWatchHandle>)>>(\n          'isar_stop_watching');\n  late final _isar_stop_watching = _isar_stop_watchingPtr\n      .asFunction<void Function(ffi.Pointer<CWatchHandle>)>();\n}\n\nclass CObject extends ffi.Struct {\n  @ffi.Int64()\n  external int id;\n\n  external ffi.Pointer<ffi.Uint8> buffer;\n\n  @ffi.Uint32()\n  external int buffer_length;\n}\n\nclass CObjectSet extends ffi.Struct {\n  external ffi.Pointer<CObject> objects;\n\n  @ffi.Uint32()\n  external int length;\n}\n\nclass CIsarCollection extends ffi.Opaque {}\n\nclass CIsarTxn extends ffi.Opaque {}\n\nclass CIndexKey extends ffi.Opaque {}\n\ntypedef DartPostCObjectFnType = ffi.Pointer<\n    ffi.NativeFunction<ffi.Int8 Function(DartPort, ffi.Pointer<CDartCObject>)>>;\ntypedef DartPort = ffi.Int64;\n\nclass CDartCObject extends ffi.Opaque {}\n\nclass CFilter extends ffi.Opaque {}\n\nclass CIsarInstance extends ffi.Opaque {}\n\nclass CQueryBuilder extends ffi.Opaque {}\n\nclass CQuery extends ffi.Opaque {}\n\nclass CAggregationResult extends ffi.Opaque {}\n\nclass CWatchHandle extends ffi.Opaque {}\n\nconst int IsarIndex_MAX_STRING_INDEX_SIZE = 1024;\n\nconst int IsarObject_NULL_BYTE = 0;\n\nconst int IsarObject_NULL_BOOL = 0;\n\nconst int IsarObject_FALSE_BOOL = 1;\n\nconst int IsarObject_TRUE_BOOL = 2;\n\nconst int IsarObject_NULL_INT = -2147483648;\n\nconst int IsarObject_NULL_LONG = -9223372036854775808;\n\nconst int IsarObject_MAX_SIZE = 33554432;\n\nconst int SchemaManager_ISAR_FILE_VERSION = 2;\n"
  },
  {
    "path": "packages/isar/lib/src/native/encode_string.dart",
    "content": "import 'dart:ffi';\nimport 'dart:typed_data';\n\nconst int _oneByteLimit = 0x7f; // 7 bits\nconst int _twoByteLimit = 0x7ff; // 11 bits\nconst int _surrogateTagMask = 0xFC00;\nconst int _surrogateValueMask = 0x3FF;\nconst int _leadSurrogateMin = 0xD800;\n\n/// Encodes a Dart String to UTF8, writes it at [offset] into [buffer] and\n/// returns the number of written bytes.\n///\n/// The buffer needs to have a capacity of at least `offset + str.length * 3`.\nint encodeString(String str, Uint8List buffer, int offset) {\n  final startOffset = offset;\n  for (var stringIndex = 0; stringIndex < str.length; stringIndex++) {\n    final codeUnit = str.codeUnitAt(stringIndex);\n    // ASCII has the same representation in UTF-8 and UTF-16.\n    if (codeUnit <= _oneByteLimit) {\n      buffer[offset++] = codeUnit;\n    } else if ((codeUnit & _surrogateTagMask) == _leadSurrogateMin) {\n      // combine surrogate pair\n      final nextCodeUnit = str.codeUnitAt(++stringIndex);\n      final rune = 0x10000 + ((codeUnit & _surrogateValueMask) << 10) |\n          (nextCodeUnit & _surrogateValueMask);\n      // If the rune is encoded with 2 code-units then it must be encoded\n      // with 4 bytes in UTF-8.\n      buffer[offset++] = 0xF0 | (rune >> 18);\n      buffer[offset++] = 0x80 | ((rune >> 12) & 0x3f);\n      buffer[offset++] = 0x80 | ((rune >> 6) & 0x3f);\n      buffer[offset++] = 0x80 | (rune & 0x3f);\n    } else if (codeUnit <= _twoByteLimit) {\n      buffer[offset++] = 0xC0 | (codeUnit >> 6);\n      buffer[offset++] = 0x80 | (codeUnit & 0x3f);\n    } else {\n      buffer[offset++] = 0xE0 | (codeUnit >> 12);\n      buffer[offset++] = 0x80 | ((codeUnit >> 6) & 0x3f);\n      buffer[offset++] = 0x80 | (codeUnit & 0x3f);\n    }\n  }\n  return offset - startOffset;\n}\n\n/// @nodoc\nextension CString on String {\n  /// Create a zero terminated C-String from a Dart String\n  Pointer<Char> toCString(Allocator alloc) {\n    final bufferPtr = alloc<Uint8>(length * 3 + 1);\n    final buffer = bufferPtr.asTypedList(length * 3 + 1);\n    final size = encodeString(this, buffer, 0);\n    buffer[size] = 0;\n    return bufferPtr.cast();\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/index_key.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:ffi';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/isar_writer_impl.dart';\n\nfinal _keyPtrPtr = malloc<Pointer<CIndexKey>>();\n\nPointer<CIndexKey> buildIndexKey(\n  CollectionSchema<dynamic> schema,\n  IndexSchema index,\n  IndexKey key,\n) {\n  if (key.length > index.properties.length) {\n    throw IsarError('Invalid number of values for index ${index.name}.');\n  }\n\n  IC.isar_key_create(_keyPtrPtr);\n  final keyPtr = _keyPtrPtr.value;\n\n  for (var i = 0; i < key.length; i++) {\n    final indexProperty = index.properties[i];\n    _addKeyValue(\n      keyPtr,\n      key[i],\n      schema.property(indexProperty.name),\n      indexProperty.type,\n      indexProperty.caseSensitive,\n    );\n  }\n\n  return keyPtr;\n}\n\nPointer<CIndexKey> buildLowerUnboundedIndexKey() {\n  IC.isar_key_create(_keyPtrPtr);\n  return _keyPtrPtr.value;\n}\n\nPointer<CIndexKey> buildUpperUnboundedIndexKey() {\n  IC.isar_key_create(_keyPtrPtr);\n  final keyPtr = _keyPtrPtr.value;\n  IC.isar_key_add_long(keyPtr, maxLong);\n\n  return keyPtr;\n}\n\nvoid _addKeyValue(\n  Pointer<CIndexKey> keyPtr,\n  Object? value,\n  PropertySchema property,\n  IndexType type,\n  bool caseSensitive,\n) {\n  if (property.enumMap != null) {\n    if (value is Enum) {\n      value = property.enumMap![value.name];\n    } else if (value is List) {\n      value = value.map((e) {\n        if (e is Enum) {\n          return property.enumMap![e.name];\n        } else {\n          return e;\n        }\n      }).toList();\n    }\n  }\n\n  final isarType =\n      type != IndexType.hash ? property.type.scalarType : property.type;\n  switch (isarType) {\n    case IsarType.bool:\n      IC.isar_key_add_byte(keyPtr, (value as bool?).byteValue);\n      break;\n    case IsarType.byte:\n      IC.isar_key_add_byte(keyPtr, (value ?? 0) as int);\n      break;\n    case IsarType.int:\n      IC.isar_key_add_int(keyPtr, (value as int?) ?? nullInt);\n      break;\n    case IsarType.float:\n      IC.isar_key_add_float(keyPtr, (value as double?) ?? nullFloat);\n      break;\n    case IsarType.long:\n      IC.isar_key_add_long(keyPtr, (value as int?) ?? nullLong);\n      break;\n    case IsarType.double:\n      IC.isar_key_add_double(keyPtr, (value as double?) ?? nullDouble);\n      break;\n    case IsarType.dateTime:\n      IC.isar_key_add_long(keyPtr, (value as DateTime?).longValue);\n      break;\n    case IsarType.string:\n      final strPtr = _strToNative(value as String?);\n      if (type == IndexType.value) {\n        IC.isar_key_add_string(keyPtr, strPtr, caseSensitive);\n      } else {\n        IC.isar_key_add_string_hash(keyPtr, strPtr, caseSensitive);\n      }\n      _freeStr(strPtr);\n      break;\n    case IsarType.boolList:\n      if (value == null) {\n        IC.isar_key_add_byte_list_hash(keyPtr, nullptr, 0);\n      } else {\n        value as List<bool?>;\n        final boolListPtr = malloc<Uint8>(value.length);\n        boolListPtr\n            .asTypedList(value.length)\n            .setAll(0, value.map((e) => e.byteValue));\n        IC.isar_key_add_byte_list_hash(keyPtr, boolListPtr, value.length);\n        malloc.free(boolListPtr);\n      }\n      break;\n    case IsarType.byteList:\n      if (value == null) {\n        IC.isar_key_add_byte_list_hash(keyPtr, nullptr, 0);\n      } else {\n        value as List<int>;\n        final bytesPtr = malloc<Uint8>(value.length);\n        bytesPtr.asTypedList(value.length).setAll(0, value);\n        IC.isar_key_add_byte_list_hash(keyPtr, bytesPtr, value.length);\n        malloc.free(bytesPtr);\n      }\n      break;\n    case IsarType.intList:\n      if (value == null) {\n        IC.isar_key_add_int_list_hash(keyPtr, nullptr, 0);\n      } else {\n        value as List<int?>;\n        final intListPtr = malloc<Int32>(value.length);\n        intListPtr\n            .asTypedList(value.length)\n            .setAll(0, value.map((e) => e ?? nullInt));\n        IC.isar_key_add_int_list_hash(keyPtr, intListPtr, value.length);\n        malloc.free(intListPtr);\n      }\n      break;\n    case IsarType.longList:\n      if (value == null) {\n        IC.isar_key_add_long_list_hash(keyPtr, nullptr, 0);\n      } else {\n        value as List<int?>;\n        final longListPtr = malloc<Int64>(value.length);\n        longListPtr\n            .asTypedList(value.length)\n            .setAll(0, value.map((e) => e ?? nullLong));\n        IC.isar_key_add_long_list_hash(keyPtr, longListPtr, value.length);\n        malloc.free(longListPtr);\n      }\n      break;\n    case IsarType.dateTimeList:\n      if (value == null) {\n        IC.isar_key_add_long_list_hash(keyPtr, nullptr, 0);\n      } else {\n        value as List<DateTime?>;\n        final longListPtr = malloc<Int64>(value.length);\n        for (var i = 0; i < value.length; i++) {\n          longListPtr[i] = value[i].longValue;\n        }\n        IC.isar_key_add_long_list_hash(keyPtr, longListPtr, value.length);\n      }\n      break;\n    case IsarType.stringList:\n      if (value == null) {\n        IC.isar_key_add_string_list_hash(keyPtr, nullptr, 0, false);\n      } else {\n        value as List<String?>;\n        final stringListPtr = malloc<Pointer<Char>>(value.length);\n        for (var i = 0; i < value.length; i++) {\n          stringListPtr[i] = _strToNative(value[i]);\n        }\n        IC.isar_key_add_string_list_hash(\n          keyPtr,\n          stringListPtr,\n          value.length,\n          caseSensitive,\n        );\n        for (var i = 0; i < value.length; i++) {\n          _freeStr(stringListPtr[i]);\n        }\n      }\n      break;\n    case IsarType.object:\n    case IsarType.floatList:\n    case IsarType.doubleList:\n    case IsarType.objectList:\n      throw IsarError('Unsupported property type.');\n  }\n}\n\nPointer<Char> _strToNative(String? str) {\n  if (str == null) {\n    return Pointer.fromAddress(0);\n  } else {\n    return str.toCString(malloc);\n  }\n}\n\nvoid _freeStr(Pointer<Char> strPtr) {\n  if (!strPtr.isNull) {\n    malloc.free(strPtr);\n  }\n}\n\ndouble? adjustFloatBound({\n  required double? value,\n  required bool lowerBound,\n  required bool include,\n  required double epsilon,\n}) {\n  value ??= double.nan;\n\n  if (lowerBound) {\n    if (include) {\n      if (value.isFinite) {\n        return value - epsilon;\n      }\n    } else {\n      if (value.isNaN) {\n        return double.negativeInfinity;\n      } else if (value == double.negativeInfinity) {\n        return -double.maxFinite;\n      } else if (value == double.maxFinite) {\n        return double.infinity;\n      } else if (value == double.infinity) {\n        return null;\n      } else {\n        return value + epsilon;\n      }\n    }\n  } else {\n    if (include) {\n      if (value.isFinite) {\n        return value + epsilon;\n      }\n    } else {\n      if (value.isNaN) {\n        return null;\n      } else if (value == double.negativeInfinity) {\n        return double.nan;\n      } else if (value == -double.maxFinite) {\n        return double.negativeInfinity;\n      } else if (value == double.infinity) {\n        return double.maxFinite;\n      } else {\n        return value - epsilon;\n      }\n    }\n  }\n  return value;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/isar_collection_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs, invalid_use_of_protected_member\n\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:ffi';\nimport 'dart:isolate';\nimport 'dart:typed_data';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/index_key.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/isar_impl.dart';\nimport 'package:isar/src/native/isar_reader_impl.dart';\nimport 'package:isar/src/native/isar_writer_impl.dart';\nimport 'package:isar/src/native/query_build.dart';\nimport 'package:isar/src/native/txn.dart';\n\nclass IsarCollectionImpl<OBJ> extends IsarCollection<OBJ> {\n  IsarCollectionImpl({\n    required this.isar,\n    required this.ptr,\n    required this.schema,\n  });\n\n  @override\n  final IsarImpl isar;\n  final Pointer<CIsarCollection> ptr;\n\n  @override\n  final CollectionSchema<OBJ> schema;\n\n  late final _offsets = isar.offsets[OBJ]!;\n  late final _staticSize = _offsets.last;\n\n  @pragma('vm:prefer-inline')\n  OBJ deserializeObject(CObject cObj) {\n    final buffer = cObj.buffer.asTypedList(cObj.buffer_length);\n    final reader = IsarReaderImpl(buffer);\n    final object = schema.deserialize(\n      cObj.id,\n      reader,\n      _offsets,\n      isar.offsets,\n    );\n    schema.attach(this, cObj.id, object);\n    return object;\n  }\n\n  @pragma('vm:prefer-inline')\n  OBJ? deserializeObjectOrNull(CObject cObj) {\n    if (!cObj.buffer.isNull) {\n      return deserializeObject(cObj);\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  List<OBJ> deserializeObjects(CObjectSet objectSet) {\n    final objects = <OBJ>[];\n    for (var i = 0; i < objectSet.length; i++) {\n      final cObjPtr = objectSet.objects.elementAt(i);\n      final object = deserializeObject(cObjPtr.ref);\n      objects.add(object);\n    }\n    return objects;\n  }\n\n  @pragma('vm:prefer-inline')\n  List<OBJ?> deserializeObjectsOrNull(CObjectSet objectSet) {\n    final objects = List<OBJ?>.filled(objectSet.length, null);\n    for (var i = 0; i < objectSet.length; i++) {\n      final cObj = objectSet.objects.elementAt(i).ref;\n      if (!cObj.buffer.isNull) {\n        objects[i] = deserializeObject(cObj);\n      }\n    }\n    return objects;\n  }\n\n  @pragma('vm:prefer-inline')\n  Pointer<Pointer<CIndexKey>> _getKeysPtr(\n    String indexName,\n    List<IndexKey> keys,\n    Allocator alloc,\n  ) {\n    final keysPtrPtr = alloc<Pointer<CIndexKey>>(keys.length);\n    for (var i = 0; i < keys.length; i++) {\n      keysPtrPtr[i] = buildIndexKey(schema, schema.index(indexName), keys[i]);\n    }\n    return keysPtrPtr;\n  }\n\n  List<T> deserializeProperty<T>(CObjectSet objectSet, int? propertyId) {\n    final values = <T>[];\n    if (propertyId != null) {\n      final propertyOffset = _offsets[propertyId];\n      for (var i = 0; i < objectSet.length; i++) {\n        final cObj = objectSet.objects.elementAt(i).ref;\n        final buffer = cObj.buffer.asTypedList(cObj.buffer_length);\n        values.add(\n          schema.deserializeProp(\n            IsarReaderImpl(buffer),\n            propertyId,\n            propertyOffset,\n            isar.offsets,\n          ) as T,\n        );\n      }\n    } else {\n      for (var i = 0; i < objectSet.length; i++) {\n        final cObj = objectSet.objects.elementAt(i).ref;\n        values.add(cObj.id as T);\n      }\n    }\n    return values;\n  }\n\n  void serializeObjects(\n    Txn txn,\n    Pointer<CObject> objectsPtr,\n    List<OBJ> objects,\n  ) {\n    var maxBufferSize = 0;\n    for (var i = 0; i < objects.length; i++) {\n      final object = objects[i];\n      maxBufferSize += schema.estimateSize(object, _offsets, isar.offsets);\n    }\n    final bufferPtr = txn.alloc<Uint8>(maxBufferSize);\n    final buffer = bufferPtr.asTypedList(maxBufferSize).buffer;\n\n    var writtenBytes = 0;\n    for (var i = 0; i < objects.length; i++) {\n      final objBuffer = buffer.asUint8List(writtenBytes);\n      final binaryWriter = IsarWriterImpl(objBuffer, _staticSize);\n\n      final object = objects[i];\n      schema.serialize(\n        object,\n        binaryWriter,\n        _offsets,\n        isar.offsets,\n      );\n      final size = binaryWriter.usedBytes;\n\n      final cObj = objectsPtr.elementAt(i).ref;\n      cObj.id = schema.getId(object);\n      cObj.buffer = bufferPtr.elementAt(writtenBytes);\n      cObj.buffer_length = size;\n\n      writtenBytes += size;\n    }\n  }\n\n  @override\n  Future<List<OBJ?>> getAll(List<int> ids) {\n    return isar.getTxn(false, (Txn txn) async {\n      final cObjSetPtr = txn.newCObjectSet(ids.length);\n      final objectsPtr = cObjSetPtr.ref.objects;\n      for (var i = 0; i < ids.length; i++) {\n        objectsPtr.elementAt(i).ref.id = ids[i];\n      }\n      IC.isar_get_all(ptr, txn.ptr, cObjSetPtr);\n      await txn.wait();\n      return deserializeObjectsOrNull(cObjSetPtr.ref);\n    });\n  }\n\n  @override\n  List<OBJ?> getAllSync(List<int> ids) {\n    return isar.getTxnSync(false, (Txn txn) {\n      final cObjPtr = txn.getCObject();\n      final cObj = cObjPtr.ref;\n\n      final objects = List<OBJ?>.filled(ids.length, null);\n      for (var i = 0; i < ids.length; i++) {\n        cObj.id = ids[i];\n        nCall(IC.isar_get(ptr, txn.ptr, cObjPtr));\n        objects[i] = deserializeObjectOrNull(cObj);\n      }\n\n      return objects;\n    });\n  }\n\n  @override\n  Future<List<OBJ?>> getAllByIndex(String indexName, List<IndexKey> keys) {\n    return isar.getTxn(false, (Txn txn) async {\n      final cObjSetPtr = txn.newCObjectSet(keys.length);\n      final keysPtrPtr = _getKeysPtr(indexName, keys, txn.alloc);\n      IC.isar_get_all_by_index(\n        ptr,\n        txn.ptr,\n        schema.index(indexName).id,\n        keysPtrPtr,\n        cObjSetPtr,\n      );\n      await txn.wait();\n      return deserializeObjectsOrNull(cObjSetPtr.ref);\n    });\n  }\n\n  @override\n  List<OBJ?> getAllByIndexSync(String indexName, List<IndexKey> keys) {\n    final index = schema.index(indexName);\n\n    return isar.getTxnSync(false, (Txn txn) {\n      final cObjPtr = txn.getCObject();\n      final cObj = cObjPtr.ref;\n\n      final objects = List<OBJ?>.filled(keys.length, null);\n      for (var i = 0; i < keys.length; i++) {\n        final keyPtr = buildIndexKey(schema, index, keys[i]);\n        nCall(IC.isar_get_by_index(ptr, txn.ptr, index.id, keyPtr, cObjPtr));\n        objects[i] = deserializeObjectOrNull(cObj);\n      }\n\n      return objects;\n    });\n  }\n\n  @override\n  int putSync(OBJ object, {bool saveLinks = true}) {\n    return isar.getTxnSync(true, (Txn txn) {\n      return putByIndexSyncInternal(\n        txn: txn,\n        object: object,\n        saveLinks: saveLinks,\n      );\n    });\n  }\n\n  @override\n  int putByIndexSync(String indexName, OBJ object, {bool saveLinks = true}) {\n    return isar.getTxnSync(true, (Txn txn) {\n      return putByIndexSyncInternal(\n        txn: txn,\n        object: object,\n        indexId: schema.index(indexName).id,\n        saveLinks: saveLinks,\n      );\n    });\n  }\n\n  int putByIndexSyncInternal({\n    required Txn txn,\n    int? indexId,\n    required OBJ object,\n    bool saveLinks = true,\n  }) {\n    final cObjPtr = txn.getCObject();\n    final cObj = cObjPtr.ref;\n\n    final estimatedSize = schema.estimateSize(object, _offsets, isar.offsets);\n    cObj.buffer = txn.getBuffer(estimatedSize);\n    final buffer = cObj.buffer.asTypedList(estimatedSize);\n\n    final writer = IsarWriterImpl(buffer, _staticSize);\n    schema.serialize(\n      object,\n      writer,\n      _offsets,\n      isar.offsets,\n    );\n    cObj.buffer_length = writer.usedBytes;\n\n    cObj.id = schema.getId(object);\n\n    if (indexId != null) {\n      nCall(IC.isar_put_by_index(ptr, txn.ptr, indexId, cObjPtr));\n    } else {\n      nCall(IC.isar_put(ptr, txn.ptr, cObjPtr));\n    }\n\n    final id = cObj.id;\n    schema.attach(this, id, object);\n\n    if (saveLinks) {\n      for (final link in schema.getLinks(object)) {\n        link.saveSync();\n      }\n    }\n\n    return id;\n  }\n\n  @override\n  Future<List<int>> putAll(List<OBJ> objects) {\n    return putAllByIndex(null, objects);\n  }\n\n  @override\n  List<int> putAllSync(List<OBJ> objects, {bool saveLinks = true}) {\n    return putAllByIndexSync(null, objects, saveLinks: saveLinks);\n  }\n\n  @override\n  Future<List<int>> putAllByIndex(String? indexName, List<OBJ> objects) {\n    final indexId = indexName != null ? schema.index(indexName).id : null;\n\n    return isar.getTxn(true, (Txn txn) async {\n      final cObjSetPtr = txn.newCObjectSet(objects.length);\n      serializeObjects(txn, cObjSetPtr.ref.objects, objects);\n\n      if (indexId != null) {\n        IC.isar_put_all_by_index(ptr, txn.ptr, indexId, cObjSetPtr);\n      } else {\n        IC.isar_put_all(ptr, txn.ptr, cObjSetPtr);\n      }\n\n      await txn.wait();\n      final cObjectSet = cObjSetPtr.ref;\n      final ids = List<int>.filled(objects.length, 0);\n      for (var i = 0; i < objects.length; i++) {\n        final cObjPtr = cObjectSet.objects.elementAt(i);\n        final id = cObjPtr.ref.id;\n        ids[i] = id;\n\n        final object = objects[i];\n        schema.attach(this, id, object);\n      }\n      return ids;\n    });\n  }\n\n  @override\n  List<int> putAllByIndexSync(\n    String? indexName,\n    List<OBJ> objects, {\n    bool saveLinks = true,\n  }) {\n    final indexId = indexName != null ? schema.index(indexName).id : null;\n    final ids = List.filled(objects.length, 0);\n    isar.getTxnSync(true, (Txn txn) {\n      for (var i = 0; i < objects.length; i++) {\n        ids[i] = putByIndexSyncInternal(\n          txn: txn,\n          object: objects[i],\n          indexId: indexId,\n          saveLinks: saveLinks,\n        );\n      }\n    });\n    return ids;\n  }\n\n  @override\n  Future<int> deleteAll(List<int> ids) {\n    return isar.getTxn(true, (Txn txn) async {\n      final countPtr = txn.alloc<Uint32>();\n      final idsPtr = txn.alloc<Int64>(ids.length);\n      idsPtr.asTypedList(ids.length).setAll(0, ids);\n\n      IC.isar_delete_all(ptr, txn.ptr, idsPtr, ids.length, countPtr);\n      await txn.wait();\n\n      return countPtr.value;\n    });\n  }\n\n  @override\n  int deleteAllSync(List<int> ids) {\n    return isar.getTxnSync(true, (Txn txn) {\n      final deletedPtr = txn.alloc<Bool>();\n\n      var counter = 0;\n      for (var i = 0; i < ids.length; i++) {\n        nCall(IC.isar_delete(ptr, txn.ptr, ids[i], deletedPtr));\n        if (deletedPtr.value) {\n          counter++;\n        }\n      }\n      return counter;\n    });\n  }\n\n  @override\n  Future<int> deleteAllByIndex(String indexName, List<IndexKey> keys) {\n    return isar.getTxn(true, (Txn txn) async {\n      final countPtr = txn.alloc<Uint32>();\n      final keysPtrPtr = _getKeysPtr(indexName, keys, txn.alloc);\n\n      IC.isar_delete_all_by_index(\n        ptr,\n        txn.ptr,\n        schema.index(indexName).id,\n        keysPtrPtr,\n        keys.length,\n        countPtr,\n      );\n      await txn.wait();\n\n      return countPtr.value;\n    });\n  }\n\n  @override\n  int deleteAllByIndexSync(String indexName, List<IndexKey> keys) {\n    return isar.getTxnSync(true, (Txn txn) {\n      final countPtr = txn.alloc<Uint32>();\n      final keysPtrPtr = _getKeysPtr(indexName, keys, txn.alloc);\n\n      nCall(\n        IC.isar_delete_all_by_index(\n          ptr,\n          txn.ptr,\n          schema.index(indexName).id,\n          keysPtrPtr,\n          keys.length,\n          countPtr,\n        ),\n      );\n      return countPtr.value;\n    });\n  }\n\n  @override\n  Future<void> clear() {\n    return isar.getTxn(true, (Txn txn) async {\n      IC.isar_clear(ptr, txn.ptr);\n      await txn.wait();\n    });\n  }\n\n  @override\n  void clearSync() {\n    isar.getTxnSync(true, (Txn txn) {\n      nCall(IC.isar_clear(ptr, txn.ptr));\n    });\n  }\n\n  @override\n  Future<void> importJson(List<Map<String, dynamic>> json) {\n    final bytes = const Utf8Encoder().convert(jsonEncode(json));\n    return importJsonRaw(bytes);\n  }\n\n  @override\n  Future<void> importJsonRaw(Uint8List jsonBytes) {\n    return isar.getTxn(true, (Txn txn) async {\n      final bytesPtr = txn.alloc<Uint8>(jsonBytes.length);\n      bytesPtr.asTypedList(jsonBytes.length).setAll(0, jsonBytes);\n      final idNamePtr = schema.idName.toCString(txn.alloc);\n\n      IC.isar_json_import(\n        ptr,\n        txn.ptr,\n        idNamePtr,\n        bytesPtr,\n        jsonBytes.length,\n      );\n      await txn.wait();\n    });\n  }\n\n  @override\n  void importJsonSync(List<Map<String, dynamic>> json) {\n    final bytes = const Utf8Encoder().convert(jsonEncode(json));\n    importJsonRawSync(bytes);\n  }\n\n  @override\n  void importJsonRawSync(Uint8List jsonBytes) {\n    return isar.getTxnSync(true, (Txn txn) async {\n      final bytesPtr = txn.getBuffer(jsonBytes.length);\n      bytesPtr.asTypedList(jsonBytes.length).setAll(0, jsonBytes);\n      final idNamePtr = schema.idName.toCString(txn.alloc);\n\n      nCall(\n        IC.isar_json_import(\n          ptr,\n          txn.ptr,\n          idNamePtr,\n          bytesPtr,\n          jsonBytes.length,\n        ),\n      );\n    });\n  }\n\n  @override\n  Future<int> count() {\n    return isar.getTxn(false, (Txn txn) async {\n      final countPtr = txn.alloc<Int64>();\n      IC.isar_count(ptr, txn.ptr, countPtr);\n      await txn.wait();\n      return countPtr.value;\n    });\n  }\n\n  @override\n  int countSync() {\n    return isar.getTxnSync(false, (Txn txn) {\n      final countPtr = txn.alloc<Int64>();\n      nCall(IC.isar_count(ptr, txn.ptr, countPtr));\n      return countPtr.value;\n    });\n  }\n\n  @override\n  Future<int> getSize({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) {\n    return isar.getTxn(false, (Txn txn) async {\n      final sizePtr = txn.alloc<Int64>();\n      IC.isar_get_size(ptr, txn.ptr, includeIndexes, includeLinks, sizePtr);\n      await txn.wait();\n      return sizePtr.value;\n    });\n  }\n\n  @override\n  int getSizeSync({bool includeIndexes = false, bool includeLinks = false}) {\n    return isar.getTxnSync(false, (Txn txn) {\n      final sizePtr = txn.alloc<Int64>();\n      nCall(\n        IC.isar_get_size(\n          ptr,\n          txn.ptr,\n          includeIndexes,\n          includeLinks,\n          sizePtr,\n        ),\n      );\n      return sizePtr.value;\n    });\n  }\n\n  @override\n  Stream<void> watchLazy({bool fireImmediately = false}) {\n    isar.requireOpen();\n    final port = ReceivePort();\n    final handle =\n        IC.isar_watch_collection(isar.ptr, ptr, port.sendPort.nativePort);\n    final controller = StreamController<void>(\n      onCancel: () {\n        IC.isar_stop_watching(handle);\n        port.close();\n      },\n    );\n\n    if (fireImmediately) {\n      controller.add(null);\n    }\n\n    controller.addStream(port);\n    return controller.stream;\n  }\n\n  @override\n  Stream<OBJ?> watchObject(Id id, {bool fireImmediately = false}) {\n    return watchObjectLazy(id, fireImmediately: fireImmediately)\n        .asyncMap((event) => get(id));\n  }\n\n  @override\n  Stream<void> watchObjectLazy(Id id, {bool fireImmediately = false}) {\n    isar.requireOpen();\n    final cObjPtr = malloc<CObject>();\n\n    final port = ReceivePort();\n    final handle =\n        IC.isar_watch_object(isar.ptr, ptr, id, port.sendPort.nativePort);\n    malloc.free(cObjPtr);\n\n    final controller = StreamController<void>(\n      onCancel: () {\n        IC.isar_stop_watching(handle);\n        port.close();\n      },\n    );\n\n    if (fireImmediately) {\n      controller.add(null);\n    }\n\n    controller.addStream(port);\n    return controller.stream;\n  }\n\n  @override\n  Query<T> buildQuery<T>({\n    List<WhereClause> whereClauses = const [],\n    bool whereDistinct = false,\n    Sort whereSort = Sort.asc,\n    FilterOperation? filter,\n    List<SortProperty> sortBy = const [],\n    List<DistinctProperty> distinctBy = const [],\n    int? offset,\n    int? limit,\n    String? property,\n  }) {\n    isar.requireOpen();\n    return buildNativeQuery(\n      this,\n      whereClauses,\n      whereDistinct,\n      whereSort,\n      filter,\n      sortBy,\n      distinctBy,\n      offset,\n      limit,\n      property,\n    );\n  }\n\n  @override\n  Future<void> verify(List<OBJ> objects) async {\n    await isar.verify();\n    return isar.getTxn(false, (Txn txn) async {\n      final cObjSetPtr = txn.newCObjectSet(objects.length);\n      serializeObjects(txn, cObjSetPtr.ref.objects, objects);\n\n      IC.isar_verify(ptr, txn.ptr, cObjSetPtr);\n      await txn.wait();\n    });\n  }\n\n  @override\n  Future<void> verifyLink(\n    String linkName,\n    List<int> sourceIds,\n    List<int> targetIds,\n  ) async {\n    final link = schema.link(linkName);\n\n    return isar.getTxn(false, (Txn txn) async {\n      final idsPtr = txn.alloc<Int64>(sourceIds.length + targetIds.length);\n      for (var i = 0; i < sourceIds.length; i++) {\n        idsPtr[i * 2] = sourceIds[i];\n        idsPtr[i * 2 + 1] = targetIds[i];\n      }\n\n      IC.isar_link_verify(\n        ptr,\n        txn.ptr,\n        link.id,\n        idsPtr,\n        sourceIds.length + targetIds.length,\n      );\n      await txn.wait();\n    });\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/isar_core.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:async';\nimport 'dart:ffi';\nimport 'dart:io';\nimport 'dart:isolate';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/bindings.dart';\n\nconst Id isarMinId = -9223372036854775807;\n\nconst Id isarMaxId = 9223372036854775807;\n\nconst Id isarAutoIncrementId = -9223372036854775808;\n\ntypedef IsarAbi = Abi;\n\nconst int minByte = 0;\nconst int maxByte = 255;\nconst int minInt = -2147483648;\nconst int maxInt = 2147483647;\nconst int minLong = -9223372036854775808;\nconst int maxLong = 9223372036854775807;\nconst double minDouble = double.nan;\nconst double maxDouble = double.infinity;\n\nconst nullByte = IsarObject_NULL_BYTE;\nconst nullInt = IsarObject_NULL_INT;\nconst nullLong = IsarObject_NULL_LONG;\nconst nullFloat = double.nan;\nconst nullDouble = double.nan;\nfinal nullDate = DateTime.fromMillisecondsSinceEpoch(0);\n\nconst nullBool = IsarObject_NULL_BOOL;\nconst falseBool = IsarObject_FALSE_BOOL;\nconst trueBool = IsarObject_TRUE_BOOL;\n\nconst String binariesUrl = 'https://binaries.isar-community.dev';\n\nbool _isarInitialized = false;\n\n// ignore: non_constant_identifier_names\nlate final IsarCoreBindings IC;\n\ntypedef FinalizerFunction = void Function(Pointer<Void> token);\nlate final Pointer<NativeFinalizerFunction> isarClose;\nlate final Pointer<NativeFinalizerFunction> isarQueryFree;\n\nFutureOr<void> initializeCoreBinary({\n  Map<Abi, String> libraries = const {},\n  bool download = false,\n}) {\n  if (_isarInitialized) {\n    return null;\n  }\n\n  String? libraryPath;\n  if (!Platform.isIOS) {\n    libraryPath = libraries[Abi.current()] ?? Abi.current().localName;\n  }\n\n  try {\n    _initializePath(libraryPath);\n  } catch (e) {\n    if (!Platform.isAndroid && !Platform.isIOS) {\n      final downloadPath = _getLibraryDownloadPath(libraries);\n      if (download) {\n        return _downloadIsarCore(downloadPath).then((value) {\n          _initializePath(downloadPath);\n        });\n      } else {\n        // try to use the binary at the download path anyway\n        _initializePath(downloadPath);\n      }\n    } else {\n      throw IsarError(\n        'Could not initialize IsarCore library for processor architecture '\n        '\"${Abi.current()}\". If you create a Flutter app, make sure to add '\n        'isar_flutter_libs to your dependencies.\\n$e',\n      );\n    }\n  }\n}\n\nvoid _initializePath(String? libraryPath) {\n  late DynamicLibrary dylib;\n  if (Platform.isIOS) {\n    dylib = DynamicLibrary.process();\n  } else {\n    dylib = DynamicLibrary.open(libraryPath!);\n  }\n\n  final bindings = IsarCoreBindings(dylib);\n\n  final coreVersion = bindings.isar_version().cast<Utf8>().toDartString();\n  if (coreVersion != Isar.version && coreVersion != 'debug') {\n    throw IsarError(\n      'Incorrect Isar Core version: Required ${Isar.version} found '\n      '$coreVersion. Make sure to use the latest isar_flutter_libs. If you '\n      'have a Dart only project, make sure that old Isar Core binaries are '\n      'deleted.',\n    );\n  }\n\n  IC = bindings;\n  isarClose = dylib.lookup('isar_instance_close');\n  isarQueryFree = dylib.lookup('isar_q_free');\n  _isarInitialized = true;\n}\n\nString _getLibraryDownloadPath(Map<Abi, String> libraries) {\n  final providedPath = libraries[Abi.current()];\n  if (providedPath != null) {\n    return providedPath;\n  } else {\n    final name = Abi.current().localName;\n    if (Platform.script.path.isEmpty) {\n      return name;\n    }\n    var dir = Platform.script.pathSegments\n        .sublist(0, Platform.script.pathSegments.length - 1)\n        .join(Platform.pathSeparator);\n    if (!Platform.isWindows) {\n      // Not on windows, add leading platform path separator\n      dir = '${Platform.pathSeparator}$dir';\n    }\n    return '$dir${Platform.pathSeparator}$name';\n  }\n}\n\nFuture<void> _downloadIsarCore(String libraryPath) async {\n  final libraryFile = File(libraryPath);\n  // ignore: avoid_slow_async_io\n  if (await libraryFile.exists()) {\n    return;\n  }\n  final remoteName = Abi.current().remoteName;\n  final uri = Uri.parse('$binariesUrl/${Isar.version}/$remoteName');\n  final request = await HttpClient().getUrl(uri);\n  final response = await request.close();\n  if (response.statusCode != 200) {\n    throw IsarError(\n      'Could not download IsarCore library: ${response.reasonPhrase}',\n    );\n  }\n  await response.pipe(libraryFile.openWrite());\n}\n\nIsarError? isarErrorFromResult(int result) {\n  if (result != 0) {\n    final error = IC.isar_get_error(result);\n    if (error.address == 0) {\n      throw IsarError(\n        'There was an error but it could not be loaded from IsarCore.',\n      );\n    }\n    try {\n      final message = error.cast<Utf8>().toDartString();\n      return IsarError(message);\n    } finally {\n      IC.isar_free_string(error);\n    }\n  } else {\n    return null;\n  }\n}\n\n@pragma('vm:prefer-inline')\nvoid nCall(int result) {\n  final error = isarErrorFromResult(result);\n  if (error != null) {\n    throw error;\n  }\n}\n\nStream<void> wrapIsarPort(ReceivePort port) {\n  final portStreamController = StreamController<void>(onCancel: port.close);\n  port.listen((event) {\n    if (event == 0) {\n      portStreamController.add(null);\n    } else {\n      final error = isarErrorFromResult(event as int);\n      portStreamController.addError(error!);\n    }\n  });\n  return portStreamController.stream;\n}\n\nextension PointerX on Pointer {\n  @pragma('vm:prefer-inline')\n  bool get isNull => address == 0;\n}\n\nextension on Abi {\n  String get localName {\n    switch (Abi.current()) {\n      case Abi.androidArm:\n      case Abi.androidArm64:\n      case Abi.androidIA32:\n      case Abi.androidX64:\n        return 'libisar.so';\n      case Abi.macosArm64:\n      case Abi.macosX64:\n        return 'libisar.dylib';\n      case Abi.linuxX64:\n        return 'libisar.so';\n      case Abi.windowsArm64:\n      case Abi.windowsX64:\n        return 'isar.dll';\n      default:\n        throw IsarError(\n          'Unsupported processor architecture \"${Abi.current()}\". '\n          'Please open an issue on GitHub to request it.',\n        );\n    }\n  }\n\n  String get remoteName {\n    switch (Abi.current()) {\n      case Abi.macosArm64:\n      case Abi.macosX64:\n        return 'libisar_macos.dylib';\n      case Abi.linuxX64:\n        return 'libisar_linux_x64.so';\n      case Abi.windowsArm64:\n        return 'isar_windows_arm64.dll';\n      case Abi.windowsX64:\n        return 'isar_windows_x64.dll';\n    }\n    throw UnimplementedError();\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/isar_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:async';\nimport 'dart:ffi';\nimport 'dart:isolate';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/src/common/isar_common.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/txn.dart';\n\nclass IsarImpl extends IsarCommon implements Finalizable {\n  IsarImpl(super.name, this.ptr) {\n    _finalizer = NativeFinalizer(isarClose);\n    _finalizer.attach(this, ptr.cast(), detach: this);\n  }\n\n  final Pointer<CIsarInstance> ptr;\n  late final NativeFinalizer _finalizer;\n\n  final offsets = <Type, List<int>>{};\n\n  final Pointer<Pointer<CIsarTxn>> _syncTxnPtrPtr = malloc<Pointer<CIsarTxn>>();\n\n  String? _directory;\n\n  @override\n  String get directory {\n    requireOpen();\n\n    if (_directory == null) {\n      final dirPtr = IC.isar_instance_get_path(ptr);\n      try {\n        _directory = dirPtr.cast<Utf8>().toDartString();\n      } finally {\n        IC.isar_free_string(dirPtr);\n      }\n    }\n\n    return _directory!;\n  }\n\n  @override\n  Future<Transaction> beginTxn(bool write, bool silent) async {\n    final port = ReceivePort();\n    final portStream = wrapIsarPort(port);\n\n    final txnPtrPtr = malloc<Pointer<CIsarTxn>>();\n    IC.isar_txn_begin(\n      ptr,\n      txnPtrPtr,\n      false,\n      write,\n      silent,\n      port.sendPort.nativePort,\n    );\n\n    final txn = Txn.async(this, txnPtrPtr.value, write, portStream);\n    await txn.wait();\n    return txn;\n  }\n\n  @override\n  Transaction beginTxnSync(bool write, bool silent) {\n    nCall(IC.isar_txn_begin(ptr, _syncTxnPtrPtr, true, write, silent, 0));\n    return Txn.sync(this, _syncTxnPtrPtr.value, write);\n  }\n\n  @override\n  bool performClose(bool deleteFromDisk) {\n    _finalizer.detach(this);\n    if (deleteFromDisk) {\n      return IC.isar_instance_close_and_delete(ptr);\n    } else {\n      return IC.isar_instance_close(ptr);\n    }\n  }\n\n  @override\n  Future<int> getSize({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) {\n    return getTxn(false, (Txn txn) async {\n      final sizePtr = txn.alloc<Int64>();\n      IC.isar_instance_get_size(\n        ptr,\n        txn.ptr,\n        includeIndexes,\n        includeLinks,\n        sizePtr,\n      );\n      await txn.wait();\n      return sizePtr.value;\n    });\n  }\n\n  @override\n  int getSizeSync({bool includeIndexes = false, bool includeLinks = false}) {\n    return getTxnSync(false, (Txn txn) {\n      final sizePtr = txn.alloc<Int64>();\n      nCall(\n        IC.isar_instance_get_size(\n          ptr,\n          txn.ptr,\n          includeIndexes,\n          includeLinks,\n          sizePtr,\n        ),\n      );\n      return sizePtr.value;\n    });\n  }\n\n  @override\n  Future<void> copyToFile(String targetPath) async {\n    final pathPtr = targetPath.toCString(malloc);\n    final receivePort = ReceivePort();\n    final nativePort = receivePort.sendPort.nativePort;\n\n    try {\n      final stream = wrapIsarPort(receivePort);\n      IC.isar_instance_copy_to_file(ptr, pathPtr, nativePort);\n      await stream.first;\n    } finally {\n      malloc.free(pathPtr);\n    }\n  }\n\n  @override\n  Future<void> verify() async {\n    return getTxn(false, (Txn txn) async {\n      IC.isar_instance_verify(ptr, txn.ptr);\n      await txn.wait();\n    });\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/isar_link_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:ffi';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/common/isar_link_base_impl.dart';\nimport 'package:isar/src/common/isar_link_common.dart';\nimport 'package:isar/src/common/isar_links_common.dart';\nimport 'package:isar/src/native/isar_collection_impl.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/txn.dart';\n\nmixin IsarLinkBaseMixin<OBJ> on IsarLinkBaseImpl<OBJ> {\n  @override\n  IsarCollectionImpl<dynamic> get sourceCollection =>\n      super.sourceCollection as IsarCollectionImpl;\n\n  @override\n  IsarCollectionImpl<OBJ> get targetCollection =>\n      super.targetCollection as IsarCollectionImpl<OBJ>;\n\n  late final int linkId = sourceCollection.schema.link(linkName).id;\n\n  @override\n  late final getId = targetCollection.schema.getId;\n\n  @override\n  Future<void> update({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  }) {\n    final linkList = link.toList();\n    final unlinkList = unlink.toList();\n\n    final containingId = requireAttached();\n    return targetCollection.isar.getTxn(true, (Txn txn) {\n      final count = linkList.length + unlinkList.length;\n      final idsPtr = txn.alloc<Int64>(count);\n      final ids = idsPtr.asTypedList(count);\n\n      for (var i = 0; i < linkList.length; i++) {\n        ids[i] = requireGetId(linkList[i]);\n      }\n      for (var i = 0; i < unlinkList.length; i++) {\n        ids[linkList.length + i] = requireGetId(unlinkList[i]);\n      }\n\n      IC.isar_link_update_all(\n        sourceCollection.ptr,\n        txn.ptr,\n        linkId,\n        containingId,\n        idsPtr,\n        linkList.length,\n        unlinkList.length,\n        reset,\n      );\n      return txn.wait();\n    });\n  }\n\n  @override\n  void updateSync({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  }) {\n    final containingId = requireAttached();\n    targetCollection.isar.getTxnSync(true, (Txn txn) {\n      if (reset) {\n        nCall(\n          IC.isar_link_unlink_all(\n            sourceCollection.ptr,\n            txn.ptr,\n            linkId,\n            containingId,\n          ),\n        );\n      }\n\n      for (final object in link) {\n        var id = getId(object);\n        if (id == Isar.autoIncrement) {\n          id = targetCollection.putByIndexSyncInternal(\n            txn: txn,\n            object: object,\n          );\n        }\n\n        nCall(\n          IC.isar_link(\n            sourceCollection.ptr,\n            txn.ptr,\n            linkId,\n            containingId,\n            id,\n          ),\n        );\n      }\n      for (final object in unlink) {\n        final unlinkId = requireGetId(object);\n        nCall(\n          IC.isar_link_unlink(\n            sourceCollection.ptr,\n            txn.ptr,\n            linkId,\n            containingId,\n            unlinkId,\n          ),\n        );\n      }\n    });\n  }\n}\n\nclass IsarLinkImpl<OBJ> extends IsarLinkCommon<OBJ>\n    with IsarLinkBaseMixin<OBJ> {}\n\nclass IsarLinksImpl<OBJ> extends IsarLinksCommon<OBJ>\n    with IsarLinkBaseMixin<OBJ> {}\n"
  },
  {
    "path": "packages/isar/lib/src/native/isar_reader_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:meta/meta.dart';\n\n/// @nodoc\n@protected\nclass IsarReaderImpl implements IsarReader {\n  IsarReaderImpl(this._buffer)\n      : _byteData = ByteData.view(_buffer.buffer, _buffer.offsetInBytes) {\n    _staticSize = _byteData.getUint16(0, Endian.little);\n  }\n\n  static const Utf8Decoder utf8Decoder = Utf8Decoder();\n\n  final Uint8List _buffer;\n  final ByteData _byteData;\n  late int _staticSize;\n\n  @pragma('vm:prefer-inline')\n  bool _readBool(int offset) {\n    final value = _buffer[offset];\n    if (value == trueBool) {\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  bool readBool(int offset) {\n    if (offset >= _staticSize) {\n      return false;\n    }\n    return _readBool(offset);\n  }\n\n  @pragma('vm:prefer-inline')\n  bool? _readBoolOrNull(int offset) {\n    final value = _buffer[offset];\n    if (value == trueBool) {\n      return true;\n    } else if (value == falseBool) {\n      return false;\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  bool? readBoolOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n    return _readBoolOrNull(offset);\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  int readByte(int offset) {\n    if (offset >= _staticSize) {\n      return 0;\n    }\n    return _buffer[offset];\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  int? readByteOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n    return _buffer[offset];\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  int readInt(int offset) {\n    if (offset >= _staticSize) {\n      return nullInt;\n    }\n    return _byteData.getInt32(offset, Endian.little);\n  }\n\n  @pragma('vm:prefer-inline')\n  int? _readIntOrNull(int offset) {\n    final value = _byteData.getInt32(offset, Endian.little);\n    if (value != nullInt) {\n      return value;\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  int? readIntOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n    return _readIntOrNull(offset);\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  double readFloat(int offset) {\n    if (offset >= _staticSize) {\n      return nullDouble;\n    }\n    return _byteData.getFloat32(offset, Endian.little);\n  }\n\n  @pragma('vm:prefer-inline')\n  double? _readFloatOrNull(int offset) {\n    final value = _byteData.getFloat32(offset, Endian.little);\n    if (!value.isNaN) {\n      return value;\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  double? readFloatOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n    return _readFloatOrNull(offset);\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  int readLong(int offset) {\n    if (offset >= _staticSize) {\n      return nullLong;\n    }\n    return _byteData.getInt64(offset, Endian.little);\n  }\n\n  @pragma('vm:prefer-inline')\n  int? _readLongOrNull(int offset) {\n    final value = _byteData.getInt64(offset, Endian.little);\n    if (value != nullLong) {\n      return value;\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  int? readLongOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n    return _readLongOrNull(offset);\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  double readDouble(int offset) {\n    if (offset >= _staticSize) {\n      return nullDouble;\n    }\n    return _byteData.getFloat64(offset, Endian.little);\n  }\n\n  @pragma('vm:prefer-inline')\n  double? _readDoubleOrNull(int offset) {\n    final value = _byteData.getFloat64(offset, Endian.little);\n    if (!value.isNaN) {\n      return value;\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  double? readDoubleOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n    return _readDoubleOrNull(offset);\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  DateTime readDateTime(int offset) {\n    final time = readLongOrNull(offset);\n    return time != null\n        ? DateTime.fromMicrosecondsSinceEpoch(time, isUtc: true).toLocal()\n        : nullDate;\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  DateTime? readDateTimeOrNull(int offset) {\n    final time = readLongOrNull(offset);\n    if (time != null) {\n      return DateTime.fromMicrosecondsSinceEpoch(time, isUtc: true).toLocal();\n    } else {\n      return null;\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  int _readUint24(int offset) {\n    return _buffer[offset] |\n        _buffer[offset + 1] << 8 |\n        _buffer[offset + 2] << 16;\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  String readString(int offset) {\n    return readStringOrNull(offset) ?? '';\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  String? readStringOrNull(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var bytesOffset = _readUint24(offset);\n    if (bytesOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(bytesOffset);\n    bytesOffset += 3;\n\n    return utf8Decoder.convert(_buffer, bytesOffset, bytesOffset + length);\n  }\n\n  @pragma('vm:prefer-inline')\n  @override\n  T? readObjectOrNull<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n  ) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var bytesOffset = _readUint24(offset);\n    if (bytesOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(bytesOffset);\n    bytesOffset += 3;\n\n    final buffer =\n        Uint8List.sublistView(_buffer, bytesOffset, bytesOffset + length);\n    final reader = IsarReaderImpl(buffer);\n    final offsets = allOffsets[T]!;\n    return deserialize(0, reader, offsets, allOffsets);\n  }\n\n  @override\n  List<bool>? readBoolList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List<bool>.filled(length, false);\n    for (var i = 0; i < length; i++) {\n      list[i] = _readBool(listOffset + i);\n    }\n    return list;\n  }\n\n  @override\n  List<bool?>? readBoolOrNullList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List<bool?>.filled(length, null);\n    for (var i = 0; i < length; i++) {\n      list[i] = _readBoolOrNull(listOffset + i);\n    }\n    return list;\n  }\n\n  @override\n  List<int>? readByteList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    return _buffer.sublist(listOffset, listOffset + length);\n  }\n\n  @override\n  List<int>? readIntList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = Int32List(length);\n    for (var i = 0; i < length; i++) {\n      list[i] = _byteData.getInt32(listOffset + i * 4, Endian.little);\n    }\n    return list;\n  }\n\n  @override\n  List<int?>? readIntOrNullList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List<int?>.filled(length, null);\n    for (var i = 0; i < length; i++) {\n      list[i] = _readIntOrNull(listOffset + i * 4);\n    }\n    return list;\n  }\n\n  @override\n  List<double>? readFloatList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = Float32List(length);\n    for (var i = 0; i < length; i++) {\n      list[i] = _byteData.getFloat32(listOffset + i * 4, Endian.little);\n    }\n    return list;\n  }\n\n  @override\n  List<double?>? readFloatOrNullList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List<double?>.filled(length, null);\n    for (var i = 0; i < length; i++) {\n      list[i] = _readFloatOrNull(listOffset + i * 4);\n    }\n    return list;\n  }\n\n  @override\n  List<int>? readLongList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = Int64List(length);\n    for (var i = 0; i < length; i++) {\n      list[i] = _byteData.getInt64(listOffset + i * 8, Endian.little);\n    }\n    return list;\n  }\n\n  @override\n  List<int?>? readLongOrNullList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List<int?>.filled(length, null);\n    for (var i = 0; i < length; i++) {\n      list[i] = _readLongOrNull(listOffset + i * 8);\n    }\n    return list;\n  }\n\n  @override\n  List<double>? readDoubleList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = Float64List(length);\n    for (var i = 0; i < length; i++) {\n      list[i] = _byteData.getFloat64(listOffset + i * 8, Endian.little);\n    }\n    return list;\n  }\n\n  @override\n  List<double?>? readDoubleOrNullList(int offset) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List<double?>.filled(length, null);\n    for (var i = 0; i < length; i++) {\n      list[i] = _readDoubleOrNull(listOffset + i * 8);\n    }\n    return list;\n  }\n\n  @override\n  List<DateTime>? readDateTimeList(int offset) {\n    return readLongOrNullList(offset)?.map((e) {\n      if (e != null) {\n        return DateTime.fromMicrosecondsSinceEpoch(e, isUtc: true).toLocal();\n      } else {\n        return nullDate;\n      }\n    }).toList();\n  }\n\n  @override\n  List<DateTime?>? readDateTimeOrNullList(int offset) {\n    return readLongOrNullList(offset)?.map((e) {\n      if (e != null) {\n        return DateTime.fromMicrosecondsSinceEpoch(e, isUtc: true).toLocal();\n      }\n    }).toList();\n  }\n\n  List<T>? readDynamicList<T>(\n    int offset,\n    T nullValue,\n    T Function(int startOffset, int endOffset) transform,\n  ) {\n    if (offset >= _staticSize) {\n      return null;\n    }\n\n    var listOffset = _readUint24(offset);\n    if (listOffset == 0) {\n      return null;\n    }\n\n    final length = _readUint24(listOffset);\n    listOffset += 3;\n\n    final list = List.filled(length, nullValue);\n    var contentOffset = listOffset + length * 3;\n    for (var i = 0; i < length; i++) {\n      final itemSize = _readUint24(listOffset + i * 3);\n\n      if (itemSize != 0) {\n        list[i] = transform(contentOffset, contentOffset + itemSize - 1);\n        contentOffset += itemSize - 1;\n      }\n    }\n\n    return list;\n  }\n\n  @override\n  List<String>? readStringList(int offset) {\n    return readDynamicList(offset, '', (startOffset, endOffset) {\n      return utf8Decoder.convert(_buffer, startOffset, endOffset);\n    });\n  }\n\n  @override\n  List<String?>? readStringOrNullList(int offset) {\n    return readDynamicList(offset, null, (startOffset, endOffset) {\n      return utf8Decoder.convert(_buffer, startOffset, endOffset);\n    });\n  }\n\n  @override\n  List<T>? readObjectList<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n    T defaultValue,\n  ) {\n    final offsets = allOffsets[T]!;\n    return readDynamicList(offset, defaultValue, (startOffset, endOffset) {\n      final buffer = Uint8List.sublistView(_buffer, startOffset, endOffset);\n      final reader = IsarReaderImpl(buffer);\n      return deserialize(0, reader, offsets, allOffsets);\n    });\n  }\n\n  @override\n  List<T?>? readObjectOrNullList<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n  ) {\n    final offsets = allOffsets[T]!;\n    return readDynamicList(offset, null, (startOffset, endOffset) {\n      final buffer = Uint8List.sublistView(_buffer, startOffset, endOffset);\n      final reader = IsarReaderImpl(buffer);\n      return deserialize(0, reader, offsets, allOffsets);\n    });\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/isar_writer_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs, prefer_asserts_with_message,\n// avoid_positional_boolean_parameters\n\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:meta/meta.dart';\n\n/// @nodoc\n@protected\nclass IsarWriterImpl implements IsarWriter {\n  IsarWriterImpl(Uint8List buffer, int staticSize)\n      : _dynamicOffset = staticSize,\n        _buffer = buffer,\n        _byteData = ByteData.view(buffer.buffer, buffer.offsetInBytes) {\n    _byteData.setUint16(0, staticSize, Endian.little);\n\n    // Required because we don't want to persist uninitialized memory.\n    for (var i = 2; i < staticSize; i++) {\n      _buffer[i] = 0;\n    }\n  }\n\n  final Uint8List _buffer;\n\n  final ByteData _byteData;\n\n  int _dynamicOffset;\n\n  int get usedBytes => _dynamicOffset;\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeBool(int offset, bool? value) {\n    _buffer[offset] = value.byteValue;\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeByte(int offset, int value) {\n    assert(value >= minByte && value <= maxByte);\n    _buffer[offset] = value;\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeInt(int offset, int? value) {\n    value ??= nullInt;\n    assert(value >= minInt && value <= maxInt);\n    _byteData.setInt32(offset, value, Endian.little);\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeFloat(int offset, double? value) {\n    _byteData.setFloat32(offset, value ?? double.nan, Endian.little);\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeLong(int offset, int? value) {\n    _byteData.setInt64(offset, value ?? nullLong, Endian.little);\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeDouble(int offset, double? value) {\n    _byteData.setFloat64(offset, value ?? double.nan, Endian.little);\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeDateTime(int offset, DateTime? value) {\n    writeLong(offset, value?.toUtc().microsecondsSinceEpoch);\n  }\n\n  @pragma('vm:prefer-inline')\n  void _writeUint24(int offset, int value) {\n    _buffer[offset] = value;\n    _buffer[offset + 1] = value >> 8;\n    _buffer[offset + 2] = value >> 16;\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeString(int offset, String? value) {\n    if (value != null) {\n      final byteCount = encodeString(value, _buffer, _dynamicOffset + 3);\n      _writeUint24(offset, _dynamicOffset);\n      _writeUint24(_dynamicOffset, byteCount);\n      _dynamicOffset += byteCount + 3;\n    } else {\n      _writeUint24(offset, 0);\n    }\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeObject<T>(\n    int offset,\n    Map<Type, List<int>> allOffsets,\n    Serialize<T> serialize,\n    T? value,\n  ) {\n    if (value != null) {\n      final buffer = Uint8List.sublistView(_buffer, _dynamicOffset + 3);\n      final offsets = allOffsets[T]!;\n      final binaryWriter = IsarWriterImpl(buffer, offsets.last);\n      serialize(value, binaryWriter, offsets, allOffsets);\n      final byteCount = binaryWriter.usedBytes;\n      _writeUint24(offset, _dynamicOffset);\n      _writeUint24(_dynamicOffset, byteCount);\n      _dynamicOffset += byteCount + 3;\n    } else {\n      _writeUint24(offset, 0);\n    }\n  }\n\n  @pragma('vm:prefer-inline')\n  void _writeListOffset(int offset, int? length) {\n    if (length == null) {\n      _writeUint24(offset, 0);\n    } else {\n      _writeUint24(offset, _dynamicOffset);\n      _writeUint24(_dynamicOffset, length);\n      _dynamicOffset += 3;\n    }\n  }\n\n  @override\n  @pragma('vm:prefer-inline')\n  void writeByteList(int offset, List<int>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      for (var i = 0; i < values.length; i++) {\n        _buffer[_dynamicOffset++] = values[i];\n      }\n    }\n  }\n\n  @override\n  void writeBoolList(int offset, List<bool?>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      for (var i = 0; i < values.length; i++) {\n        _buffer[_dynamicOffset++] = values[i].byteValue;\n      }\n    }\n  }\n\n  @override\n  void writeIntList(int offset, List<int?>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      for (var value in values) {\n        value ??= nullInt;\n        assert(value >= minInt && value <= maxInt);\n        _byteData.setUint32(_dynamicOffset, value, Endian.little);\n        _dynamicOffset += 4;\n      }\n    }\n  }\n\n  @override\n  void writeFloatList(int offset, List<double?>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      for (var i = 0; i < values.length; i++) {\n        _byteData.setFloat32(\n          _dynamicOffset,\n          values[i] ?? nullFloat,\n          Endian.little,\n        );\n        _dynamicOffset += 4;\n      }\n    }\n  }\n\n  @override\n  void writeLongList(int offset, List<int?>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      for (var i = 0; i < values.length; i++) {\n        _byteData.setInt64(\n          _dynamicOffset,\n          values[i] ?? nullLong,\n          Endian.little,\n        );\n        _dynamicOffset += 8;\n      }\n    }\n  }\n\n  @override\n  void writeDoubleList(int offset, List<double?>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      for (var i = 0; i < values.length; i++) {\n        _byteData.setFloat64(\n          _dynamicOffset,\n          values[i] ?? nullDouble,\n          Endian.little,\n        );\n        _dynamicOffset += 8;\n      }\n    }\n  }\n\n  @override\n  void writeDateTimeList(int offset, List<DateTime?>? values) {\n    final longList = values?.map((e) => e.longValue).toList();\n    writeLongList(offset, longList);\n  }\n\n  @override\n  void writeStringList(int offset, List<String?>? values) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      final offsetListOffset = _dynamicOffset;\n      _dynamicOffset += values.length * 3;\n      for (var i = 0; i < values.length; i++) {\n        final value = values[i];\n        if (value != null) {\n          final byteCount = encodeString(value, _buffer, _dynamicOffset);\n          _writeUint24(offsetListOffset + i * 3, byteCount + 1);\n          _dynamicOffset += byteCount;\n        } else {\n          _writeUint24(offsetListOffset + i * 3, 0);\n        }\n      }\n    }\n  }\n\n  @override\n  void writeObjectList<T>(\n    int offset,\n    Map<Type, List<int>> allOffsets,\n    Serialize<T> serialize,\n    List<T?>? values,\n  ) {\n    _writeListOffset(offset, values?.length);\n\n    if (values != null) {\n      final offsetListOffset = _dynamicOffset;\n      _dynamicOffset += values.length * 3;\n\n      final offsets = allOffsets[T]!;\n      final staticSize = offsets.last;\n      for (var i = 0; i < values.length; i++) {\n        final value = values[i];\n        if (value != null) {\n          final buffer = Uint8List.sublistView(_buffer, _dynamicOffset);\n          final binaryWriter = IsarWriterImpl(buffer, staticSize);\n          serialize(value, binaryWriter, offsets, allOffsets);\n          final byteCount = binaryWriter.usedBytes;\n          _writeUint24(offsetListOffset + i * 3, byteCount + 1);\n          _dynamicOffset += byteCount;\n        } else {\n          _writeUint24(offsetListOffset + i * 3, 0);\n        }\n      }\n    }\n  }\n}\n\nextension IsarBoolValue on bool? {\n  @pragma('vm:prefer-inline')\n  int get byteValue =>\n      this == null ? nullBool : (this == true ? trueBool : falseBool);\n}\n\nextension IsarDateTimeValue on DateTime? {\n  @pragma('vm:prefer-inline')\n  int get longValue => this?.toUtc().microsecondsSinceEpoch ?? nullLong;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/open.dart",
    "content": "// ignore_for_file: public_member_api_docs, invalid_use_of_protected_member\n\nimport 'dart:convert';\nimport 'dart:ffi';\nimport 'dart:isolate';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/common/schemas.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/isar_collection_impl.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/isar_impl.dart';\n\nfinal Pointer<Pointer<CIsarInstance>> _isarPtrPtr =\n    malloc<Pointer<CIsarInstance>>();\n\nList<int> _getOffsets(\n  Pointer<CIsarCollection> colPtr,\n  int propertiesCount,\n  int embeddedColId,\n) {\n  final offsetsPtr = malloc<Uint32>(propertiesCount);\n  final staticSize = IC.isar_get_offsets(colPtr, embeddedColId, offsetsPtr);\n  final offsets = offsetsPtr.asTypedList(propertiesCount).toList();\n  offsets.add(staticSize);\n  malloc.free(offsetsPtr);\n  return offsets;\n}\n\nvoid _initializeInstance(\n  IsarImpl isar,\n  List<CollectionSchema<dynamic>> schemas,\n) {\n  final colPtrPtr = malloc<Pointer<CIsarCollection>>();\n\n  final cols = <Type, IsarCollection<dynamic>>{};\n  for (final schema in schemas) {\n    nCall(IC.isar_instance_get_collection(isar.ptr, colPtrPtr, schema.id));\n\n    final offsets = _getOffsets(colPtrPtr.value, schema.properties.length, 0);\n\n    for (final embeddedSchema in schema.embeddedSchemas.values) {\n      final embeddedType = embeddedSchema.type;\n      if (!isar.offsets.containsKey(embeddedType)) {\n        final offsets = _getOffsets(\n          colPtrPtr.value,\n          embeddedSchema.properties.length,\n          embeddedSchema.id,\n        );\n        isar.offsets[embeddedType] = offsets;\n      }\n    }\n\n    schema.toCollection(<OBJ>() {\n      isar.offsets[OBJ] = offsets;\n\n      schema as CollectionSchema<OBJ>;\n      cols[OBJ] = IsarCollectionImpl<OBJ>(\n        isar: isar,\n        ptr: colPtrPtr.value,\n        schema: schema,\n      );\n    });\n  }\n\n  malloc.free(colPtrPtr);\n\n  isar.attachCollections(cols);\n}\n\nFuture<Isar> openIsar({\n  required List<CollectionSchema<dynamic>> schemas,\n  required String directory,\n  required String name,\n  required int maxSizeMiB,\n  required bool relaxedDurability,\n  CompactCondition? compactOnLaunch,\n}) async {\n  initializeCoreBinary();\n  IC.isar_connect_dart_api(NativeApi.postCObject.cast());\n\n  return using((Arena alloc) async {\n    final namePtr = name.toCString(alloc);\n    final dirPtr = directory.toCString(alloc);\n\n    final schemasJson = getSchemas(schemas).map((e) => e.toJson());\n    final schemaStrPtr = jsonEncode(schemasJson.toList()).toCString(alloc);\n\n    final compactMinFileSize = compactOnLaunch?.minFileSize;\n    final compactMinBytes = compactOnLaunch?.minBytes;\n    final compactMinRatio =\n        compactOnLaunch == null ? double.nan : compactOnLaunch.minRatio;\n\n    final receivePort = ReceivePort();\n    final nativePort = receivePort.sendPort.nativePort;\n    final stream = wrapIsarPort(receivePort);\n    IC.isar_instance_create_async(\n      _isarPtrPtr,\n      namePtr,\n      dirPtr,\n      schemaStrPtr,\n      maxSizeMiB,\n      relaxedDurability,\n      compactMinFileSize ?? 0,\n      compactMinBytes ?? 0,\n      compactMinRatio ?? 0,\n      nativePort,\n    );\n    await stream.first;\n\n    final isar = IsarImpl(name, _isarPtrPtr.value);\n    _initializeInstance(isar, schemas);\n    return isar;\n  });\n}\n\nIsar openIsarSync({\n  required List<CollectionSchema<dynamic>> schemas,\n  required String directory,\n  required String name,\n  required int maxSizeMiB,\n  required bool relaxedDurability,\n  CompactCondition? compactOnLaunch,\n}) {\n  initializeCoreBinary();\n  IC.isar_connect_dart_api(NativeApi.postCObject.cast());\n  return using((Arena alloc) {\n    final namePtr = name.toCString(alloc);\n    final dirPtr = directory.toCString(alloc);\n\n    final schemasJson = getSchemas(schemas).map((e) => e.toJson());\n    final schemaStrPtr = jsonEncode(schemasJson.toList()).toCString(alloc);\n\n    final compactMinFileSize = compactOnLaunch?.minFileSize;\n    final compactMinBytes = compactOnLaunch?.minBytes;\n    final compactMinRatio =\n        compactOnLaunch == null ? double.nan : compactOnLaunch.minRatio;\n\n    nCall(\n      IC.isar_instance_create(\n        _isarPtrPtr,\n        namePtr,\n        dirPtr,\n        schemaStrPtr,\n        maxSizeMiB,\n        relaxedDurability,\n        compactMinFileSize ?? 0,\n        compactMinBytes ?? 0,\n        compactMinRatio ?? 0,\n      ),\n    );\n\n    final isar = IsarImpl(name, _isarPtrPtr.value);\n    _initializeInstance(isar, schemas);\n    return isar;\n  });\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/query_build.dart",
    "content": "// ignore_for_file: invalid_use_of_protected_member, public_member_api_docs\n\nimport 'dart:ffi';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/index_key.dart';\nimport 'package:isar/src/native/isar_collection_impl.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/isar_writer_impl.dart';\nimport 'package:isar/src/native/query_impl.dart';\n\nfinal Pointer<Char> maxStr = '\\u{FFFFF}'.toNativeUtf8().cast<Char>();\n\nQuery<T> buildNativeQuery<T>(\n  IsarCollectionImpl<dynamic> col,\n  List<WhereClause> whereClauses,\n  bool whereDistinct,\n  Sort whereSort,\n  FilterOperation? filter,\n  List<SortProperty> sortBy,\n  List<DistinctProperty> distinctBy,\n  int? offset,\n  int? limit,\n  String? property,\n) {\n  final qbPtr = IC.isar_qb_create(col.ptr);\n\n  for (final wc in whereClauses) {\n    if (wc is IdWhereClause) {\n      _addIdWhereClause(qbPtr, wc, whereSort);\n    } else if (wc is IndexWhereClause) {\n      _addIndexWhereClause(\n        col.schema,\n        qbPtr,\n        wc,\n        whereDistinct,\n        whereSort,\n      );\n    } else {\n      _addLinkWhereClause(col.isar, qbPtr, wc as LinkWhereClause);\n    }\n  }\n\n  if (filter != null) {\n    final alloc = Arena(malloc);\n    try {\n      final filterPtr = _buildFilter(col, null, filter, alloc);\n      if (filterPtr != null) {\n        IC.isar_qb_set_filter(qbPtr, filterPtr);\n      }\n    } finally {\n      alloc.releaseAll();\n    }\n  }\n\n  for (final sortProperty in sortBy) {\n    final property = col.schema.property(sortProperty.property);\n    nCall(\n      IC.isar_qb_add_sort_by(\n        qbPtr,\n        property.id,\n        sortProperty.sort == Sort.asc,\n      ),\n    );\n  }\n\n  if (offset != null || limit != null) {\n    IC.isar_qb_set_offset_limit(qbPtr, offset ?? -1, limit ?? -1);\n  }\n\n  for (final distinctByProperty in distinctBy) {\n    final property = col.schema.property(distinctByProperty.property);\n    nCall(\n      IC.isar_qb_add_distinct_by(\n        qbPtr,\n        property.id,\n        distinctByProperty.caseSensitive ?? true,\n      ),\n    );\n  }\n\n  QueryDeserialize<T> deserialize;\n  int? propertyId;\n  if (property == null) {\n    deserialize = (col as IsarCollectionImpl<T>).deserializeObjects;\n  } else {\n    propertyId =\n        property != col.schema.idName ? col.schema.property(property).id : null;\n    deserialize =\n        (CObjectSet cObjSet) => col.deserializeProperty(cObjSet, propertyId);\n  }\n\n  final queryPtr = IC.isar_qb_build(qbPtr);\n  return QueryImpl(col, queryPtr, deserialize, propertyId);\n}\n\nvoid _addIdWhereClause(\n  Pointer<CQueryBuilder> qbPtr,\n  IdWhereClause wc,\n  Sort sort,\n) {\n  final lower = (wc.lower ?? minLong) + (wc.includeLower ? 0 : 1);\n  final upper = (wc.upper ?? maxLong) - (wc.includeUpper ? 0 : 1);\n  nCall(\n    IC.isar_qb_add_id_where_clause(\n      qbPtr,\n      sort == Sort.asc ? lower : upper,\n      sort == Sort.asc ? upper : lower,\n    ),\n  );\n}\n\nPointer<CIndexKey>? _buildLowerIndexBound(\n  CollectionSchema<dynamic> schema,\n  IndexSchema index,\n  IndexWhereClause wc,\n) {\n  if (wc.lower == null) {\n    return buildLowerUnboundedIndexKey();\n  }\n\n  final firstVal = wc.lower!.length == 1 ? wc.lower!.first : null;\n  if (firstVal is double) {\n    final adjusted = adjustFloatBound(\n      value: firstVal,\n      lowerBound: true,\n      include: wc.includeLower,\n      epsilon: wc.epsilon,\n    );\n    if (adjusted == null) {\n      return null;\n    }\n\n    return buildIndexKey(schema, index, [adjusted]);\n  } else {\n    final lowerPtr = buildIndexKey(schema, index, wc.lower!);\n\n    if (!wc.includeLower) {\n      if (!IC.isar_key_increase(lowerPtr)) {\n        return null;\n      }\n    }\n\n    return lowerPtr;\n  }\n}\n\nPointer<CIndexKey>? _buildUpperIndexBound(\n  CollectionSchema<dynamic> schema,\n  IndexSchema index,\n  IndexWhereClause wc,\n) {\n  if (wc.upper == null) {\n    return buildUpperUnboundedIndexKey();\n  }\n\n  final firstVal = wc.upper!.length == 1 ? wc.upper!.first : null;\n  if (firstVal is double) {\n    final adjusted = adjustFloatBound(\n      value: firstVal,\n      lowerBound: false,\n      include: wc.includeUpper,\n      epsilon: wc.epsilon,\n    );\n    if (adjusted == null) {\n      return null;\n    } else {\n      return buildIndexKey(schema, index, [adjusted]);\n    }\n  } else {\n    final upperPtr = buildIndexKey(schema, index, wc.upper!);\n\n    if (!wc.includeUpper) {\n      if (!IC.isar_key_decrease(upperPtr)) {\n        return null;\n      }\n    }\n\n    // Also include composite indexes for upper keys\n    if (index.properties.length > wc.upper!.length) {\n      IC.isar_key_add_long(upperPtr, maxLong);\n    }\n\n    return upperPtr;\n  }\n}\n\nvoid _addIndexWhereClause(\n  CollectionSchema<dynamic> schema,\n  Pointer<CQueryBuilder> qbPtr,\n  IndexWhereClause wc,\n  bool distinct,\n  Sort sort,\n) {\n  final index = schema.index(wc.indexName);\n  final lowerPtr = _buildLowerIndexBound(schema, index, wc);\n  final upperPtr = _buildUpperIndexBound(schema, index, wc);\n\n  if (lowerPtr != null && upperPtr != null) {\n    nCall(\n      IC.isar_qb_add_index_where_clause(\n        qbPtr,\n        schema.index(wc.indexName).id,\n        lowerPtr,\n        upperPtr,\n        sort == Sort.asc,\n        distinct,\n      ),\n    );\n  } else {\n    // this where clause does not match any objects\n    nCall(\n      IC.isar_qb_add_id_where_clause(\n        qbPtr,\n        Isar.autoIncrement,\n        Isar.autoIncrement,\n      ),\n    );\n  }\n}\n\nvoid _addLinkWhereClause(\n  Isar isar,\n  Pointer<CQueryBuilder> qbPtr,\n  LinkWhereClause wc,\n) {\n  final linkCol = isar.getCollectionByNameInternal(wc.linkCollection)!;\n  linkCol as IsarCollectionImpl;\n\n  final linkId = linkCol.schema.link(wc.linkName).id;\n  nCall(IC.isar_qb_add_link_where_clause(qbPtr, linkCol.ptr, linkId, wc.id));\n}\n\nPointer<CFilter>? _buildFilter(\n  IsarCollectionImpl<dynamic> col,\n  Schema<dynamic>? embeddedCol,\n  FilterOperation filter,\n  Allocator alloc,\n) {\n  if (filter is FilterGroup) {\n    return _buildFilterGroup(col, embeddedCol, filter, alloc);\n  } else if (filter is LinkFilter) {\n    return _buildLink(col, filter, alloc);\n  } else if (filter is ObjectFilter) {\n    return _buildObject(col, embeddedCol, filter, alloc);\n  } else if (filter is FilterCondition) {\n    return _buildCondition(col, embeddedCol, filter, alloc);\n  } else {\n    return null;\n  }\n}\n\nPointer<CFilter>? _buildFilterGroup(\n  IsarCollectionImpl<dynamic> col,\n  Schema<dynamic>? embeddedCol,\n  FilterGroup group,\n  Allocator alloc,\n) {\n  final builtConditions = group.filters\n      .map((FilterOperation op) => _buildFilter(col, embeddedCol, op, alloc))\n      .where((Pointer<CFilter>? it) => it != null)\n      .toList();\n\n  if (builtConditions.isEmpty) {\n    return null;\n  }\n\n  final filterPtrPtr = alloc<Pointer<CFilter>>();\n  if (group.type == FilterGroupType.not) {\n    IC.isar_filter_not(\n      filterPtrPtr,\n      builtConditions.first!,\n    );\n  } else if (builtConditions.length == 1) {\n    return builtConditions[0];\n  } else {\n    final conditionsPtrPtr = alloc<Pointer<CFilter>>(builtConditions.length);\n    for (var i = 0; i < builtConditions.length; i++) {\n      conditionsPtrPtr[i] = builtConditions[i]!;\n    }\n    IC.isar_filter_and_or_xor(\n      filterPtrPtr,\n      group.type == FilterGroupType.and,\n      group.type == FilterGroupType.xor,\n      conditionsPtrPtr,\n      builtConditions.length,\n    );\n  }\n\n  return filterPtrPtr.value;\n}\n\nPointer<CFilter>? _buildLink(\n  IsarCollectionImpl<dynamic> col,\n  LinkFilter link,\n  Allocator alloc,\n) {\n  final linkSchema = col.schema.link(link.linkName);\n  final linkTargetCol =\n      col.isar.getCollectionByNameInternal(linkSchema.target)!;\n  final linkId = col.schema.link(link.linkName).id;\n\n  final filterPtrPtr = alloc<Pointer<CFilter>>();\n\n  if (link.filter != null) {\n    final condition = _buildFilter(\n      linkTargetCol as IsarCollectionImpl,\n      null,\n      link.filter!,\n      alloc,\n    );\n    if (condition == null) {\n      return null;\n    }\n\n    nCall(\n      IC.isar_filter_link(\n        col.ptr,\n        filterPtrPtr,\n        condition,\n        linkId,\n      ),\n    );\n  } else {\n    nCall(\n      IC.isar_filter_link_length(\n        col.ptr,\n        filterPtrPtr,\n        link.lower!,\n        link.upper!,\n        linkId,\n      ),\n    );\n  }\n\n  return filterPtrPtr.value;\n}\n\nPointer<CFilter>? _buildObject(\n  IsarCollectionImpl<dynamic> col,\n  Schema<dynamic>? embeddedCol,\n  ObjectFilter objectFilter,\n  Allocator alloc,\n) {\n  final property = (embeddedCol ?? col.schema).property(objectFilter.property);\n\n  final condition = _buildFilter(\n    col,\n    col.schema.embeddedSchemas[property.target],\n    objectFilter.filter,\n    alloc,\n  );\n  if (condition == null) {\n    return null;\n  }\n\n  final filterPtrPtr = alloc<Pointer<CFilter>>();\n  nCall(\n    IC.isar_filter_object(\n      col.ptr,\n      filterPtrPtr,\n      condition,\n      embeddedCol?.id ?? 0,\n      property.id,\n    ),\n  );\n\n  return filterPtrPtr.value;\n}\n\nObject _prepareValue(\n  Object? value,\n  Allocator alloc,\n  IsarType type,\n  Map<String, dynamic>? enumMap,\n) {\n  if (value is bool) {\n    return value.byteValue;\n  } else if (value is DateTime) {\n    return value.longValue;\n  } else if (value is Enum) {\n    return _prepareValue(enumMap![value.name], alloc, type, null);\n  } else if (value is String) {\n    return value.toCString(alloc);\n  } else if (value == null) {\n    switch (type) {\n      case IsarType.bool:\n      case IsarType.byte:\n      case IsarType.boolList:\n      case IsarType.byteList:\n        return minByte;\n      case IsarType.int:\n      case IsarType.intList:\n        return minInt;\n      case IsarType.long:\n      case IsarType.longList:\n      case IsarType.dateTime:\n      case IsarType.dateTimeList:\n        return minLong;\n      case IsarType.float:\n      case IsarType.double:\n      case IsarType.floatList:\n      case IsarType.doubleList:\n        return minDouble;\n      case IsarType.string:\n      case IsarType.stringList:\n      case IsarType.object:\n      case IsarType.objectList:\n        return nullptr;\n    }\n  } else {\n    return value;\n  }\n}\n\nPointer<CFilter> _buildCondition(\n  IsarCollectionImpl<dynamic> col,\n  Schema<dynamic>? embeddedCol,\n  FilterCondition condition,\n  Allocator alloc,\n) {\n  final property = condition.property != col.schema.idName\n      ? (embeddedCol ?? col.schema).property(condition.property)\n      : null;\n\n  final value1 = _prepareValue(\n    condition.value1,\n    alloc,\n    property?.type ?? IsarType.long,\n    property?.enumMap,\n  );\n  final value2 = _prepareValue(\n    condition.value2,\n    alloc,\n    property?.type ?? IsarType.long,\n    property?.enumMap,\n  );\n  final filterPtr = alloc<Pointer<CFilter>>();\n\n  switch (condition.type) {\n    case FilterConditionType.equalTo:\n      _buildConditionEqual(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        val: value1,\n        caseSensitive: condition.caseSensitive,\n        epsilon: condition.epsilon,\n      );\n      break;\n    case FilterConditionType.between:\n      _buildConditionBetween(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        lower: value1,\n        includeLower: condition.include1,\n        upper: value2,\n        includeUpper: condition.include2,\n        caseSensitive: condition.caseSensitive,\n        epsilon: condition.epsilon,\n      );\n      break;\n    case FilterConditionType.lessThan:\n      _buildConditionLessThan(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        val: value1,\n        include: condition.include1,\n        caseSensitive: condition.caseSensitive,\n        epsilon: condition.epsilon,\n      );\n      break;\n    case FilterConditionType.greaterThan:\n      _buildConditionGreaterThan(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        val: value1,\n        include: condition.include1,\n        caseSensitive: condition.caseSensitive,\n        epsilon: condition.epsilon,\n      );\n      break;\n    case FilterConditionType.startsWith:\n    case FilterConditionType.endsWith:\n    case FilterConditionType.contains:\n    case FilterConditionType.matches:\n      _buildConditionStringOp(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        conditionType: condition.type,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        val: value1,\n        include: condition.include1,\n        caseSensitive: condition.caseSensitive,\n      );\n      break;\n    case FilterConditionType.isNull:\n      _buildConditionIsNull(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n      );\n      break;\n    case FilterConditionType.isNotNull:\n      _buildConditionIsNotNull(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        alloc: alloc,\n      );\n      break;\n    case FilterConditionType.elementIsNull:\n      _buildConditionElementIsNull(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        isObjectList: property?.type == IsarType.objectList,\n        nullValue: value1,\n      );\n      break;\n    case FilterConditionType.elementIsNotNull:\n      _buildConditionElementIsNotNull(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        isObjectList: property?.type == IsarType.objectList,\n        nullValue: value1,\n        alloc: alloc,\n      );\n      break;\n    case FilterConditionType.listLength:\n      _buildListLength(\n        colPtr: col.ptr,\n        filterPtr: filterPtr,\n        embeddedColId: embeddedCol?.id,\n        propertyId: property?.id,\n        lower: value1,\n        upper: value2,\n      );\n      break;\n  }\n\n  return filterPtr.value;\n}\n\nvoid _buildConditionIsNull({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n}) {\n  if (propertyId != null) {\n    nCall(\n      IC.isar_filter_null(\n        colPtr,\n        filterPtr,\n        embeddedColId ?? 0,\n        propertyId,\n      ),\n    );\n  } else {\n    IC.isar_filter_static(filterPtr, false);\n  }\n}\n\nvoid _buildConditionIsNotNull({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Allocator alloc,\n}) {\n  if (propertyId != null) {\n    final conditionPtr = alloc<Pointer<CFilter>>();\n    nCall(\n      IC.isar_filter_null(\n        colPtr,\n        conditionPtr,\n        embeddedColId ?? 0,\n        propertyId,\n      ),\n    );\n    IC.isar_filter_not(filterPtr, conditionPtr.value);\n  } else {\n    IC.isar_filter_static(filterPtr, true);\n  }\n}\n\nvoid _buildConditionElementIsNull({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required bool isObjectList,\n  required Object nullValue,\n}) {\n  if (isObjectList) {\n    IC.isar_filter_object(\n      colPtr,\n      filterPtr,\n      nullptr,\n      embeddedColId ?? 0,\n      propertyId ?? 0,\n    );\n  } else {\n    _buildConditionEqual(\n      colPtr: colPtr,\n      filterPtr: filterPtr,\n      embeddedColId: embeddedColId,\n      propertyId: propertyId,\n      val: nullValue,\n      epsilon: 0,\n      caseSensitive: true,\n    );\n  }\n}\n\nvoid _buildConditionElementIsNotNull({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required bool isObjectList,\n  required Object nullValue,\n  required Allocator alloc,\n}) {\n  if (isObjectList) {\n    final objFilterPtrPtr = alloc<Pointer<CFilter>>();\n    IC.isar_filter_static(objFilterPtrPtr, true);\n    IC.isar_filter_object(\n      colPtr,\n      filterPtr,\n      objFilterPtrPtr.value,\n      embeddedColId ?? 0,\n      propertyId ?? 0,\n    );\n  } else {\n    _buildConditionGreaterThan(\n      colPtr: colPtr,\n      filterPtr: filterPtr,\n      embeddedColId: embeddedColId,\n      propertyId: propertyId,\n      val: nullValue,\n      include: false,\n      epsilon: 0,\n      caseSensitive: true,\n    );\n  }\n}\n\nvoid _buildConditionEqual({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Object val,\n  required bool caseSensitive,\n  required double epsilon,\n}) {\n  if (val is int) {\n    if (propertyId == null) {\n      IC.isar_filter_id(filterPtr, val, true, val, true);\n    } else {\n      nCall(\n        IC.isar_filter_long(\n          colPtr,\n          filterPtr,\n          val,\n          true,\n          val,\n          true,\n          embeddedColId ?? 0,\n          propertyId,\n        ),\n      );\n    }\n  } else if (val is double) {\n    final lower = adjustFloatBound(\n      value: val,\n      lowerBound: true,\n      include: true,\n      epsilon: epsilon,\n    );\n    final upper = adjustFloatBound(\n      value: val,\n      lowerBound: false,\n      include: true,\n      epsilon: epsilon,\n    );\n    if (lower == null || upper == null) {\n      IC.isar_filter_static(filterPtr, false);\n    } else {\n      nCall(\n        IC.isar_filter_double(\n          colPtr,\n          filterPtr,\n          lower,\n          upper,\n          embeddedColId ?? 0,\n          propertyId!,\n        ),\n      );\n    }\n  } else if (val is Pointer<Char>) {\n    nCall(\n      IC.isar_filter_string(\n        colPtr,\n        filterPtr,\n        val,\n        true,\n        val,\n        true,\n        caseSensitive,\n        embeddedColId ?? 0,\n        propertyId!,\n      ),\n    );\n  } else {\n    throw IsarError('Unsupported type for condition');\n  }\n}\n\nvoid _buildConditionBetween({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Object lower,\n  required bool includeLower,\n  required Object upper,\n  required bool includeUpper,\n  required bool caseSensitive,\n  required double epsilon,\n}) {\n  if (lower is int && upper is int) {\n    if (propertyId == null) {\n      IC.isar_filter_id(filterPtr, lower, includeLower, upper, includeUpper);\n    } else {\n      nCall(\n        IC.isar_filter_long(\n          colPtr,\n          filterPtr,\n          lower,\n          includeLower,\n          upper,\n          includeUpper,\n          embeddedColId ?? 0,\n          propertyId,\n        ),\n      );\n    }\n  } else if (lower is double && upper is double) {\n    final adjustedLower = adjustFloatBound(\n      value: lower,\n      lowerBound: true,\n      include: includeLower,\n      epsilon: epsilon,\n    );\n    final adjustedUpper = adjustFloatBound(\n      value: upper,\n      lowerBound: false,\n      include: includeUpper,\n      epsilon: epsilon,\n    );\n    if (adjustedLower == null || adjustedUpper == null) {\n      IC.isar_filter_static(filterPtr, false);\n    } else {\n      nCall(\n        IC.isar_filter_double(\n          colPtr,\n          filterPtr,\n          adjustedLower,\n          adjustedUpper,\n          embeddedColId ?? 0,\n          propertyId!,\n        ),\n      );\n    }\n  } else if (lower is Pointer<Char> && upper is Pointer<Char>) {\n    nCall(\n      IC.isar_filter_string(\n        colPtr,\n        filterPtr,\n        lower,\n        includeLower,\n        upper,\n        includeUpper,\n        caseSensitive,\n        embeddedColId ?? 0,\n        propertyId!,\n      ),\n    );\n  } else {\n    throw IsarError('Unsupported type for condition');\n  }\n}\n\nvoid _buildConditionLessThan({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Object val,\n  required bool include,\n  required bool caseSensitive,\n  required double epsilon,\n}) {\n  if (val is int) {\n    if (propertyId == null) {\n      IC.isar_filter_id(filterPtr, minLong, true, val, include);\n    } else {\n      nCall(\n        IC.isar_filter_long(\n          colPtr,\n          filterPtr,\n          minLong,\n          true,\n          val,\n          include,\n          embeddedColId ?? 0,\n          propertyId,\n        ),\n      );\n    }\n  } else if (val is double) {\n    final upper = adjustFloatBound(\n      value: val,\n      lowerBound: false,\n      include: include,\n      epsilon: epsilon,\n    );\n    if (upper == null) {\n      IC.isar_filter_static(filterPtr, false);\n    } else {\n      nCall(\n        IC.isar_filter_double(\n          colPtr,\n          filterPtr,\n          minDouble,\n          upper,\n          embeddedColId ?? 0,\n          propertyId!,\n        ),\n      );\n    }\n  } else if (val is Pointer<Char>) {\n    nCall(\n      IC.isar_filter_string(\n        colPtr,\n        filterPtr,\n        nullptr,\n        true,\n        val,\n        include,\n        caseSensitive,\n        embeddedColId ?? 0,\n        propertyId!,\n      ),\n    );\n  } else {\n    throw IsarError('Unsupported type for condition');\n  }\n}\n\nvoid _buildConditionGreaterThan({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Object val,\n  required bool include,\n  required bool caseSensitive,\n  required double epsilon,\n}) {\n  if (val is int) {\n    if (propertyId == null) {\n      IC.isar_filter_id(filterPtr, val, include, maxLong, true);\n    } else {\n      nCall(\n        IC.isar_filter_long(\n          colPtr,\n          filterPtr,\n          val,\n          include,\n          maxLong,\n          true,\n          embeddedColId ?? 0,\n          propertyId,\n        ),\n      );\n    }\n  } else if (val is double) {\n    final lower = adjustFloatBound(\n      value: val,\n      lowerBound: true,\n      include: include,\n      epsilon: epsilon,\n    );\n    if (lower == null) {\n      IC.isar_filter_static(filterPtr, false);\n    } else {\n      nCall(\n        IC.isar_filter_double(\n          colPtr,\n          filterPtr,\n          lower,\n          maxDouble,\n          embeddedColId ?? 0,\n          propertyId!,\n        ),\n      );\n    }\n  } else if (val is Pointer<Char>) {\n    nCall(\n      IC.isar_filter_string(\n        colPtr,\n        filterPtr,\n        val,\n        include,\n        maxStr,\n        true,\n        caseSensitive,\n        embeddedColId ?? 0,\n        propertyId!,\n      ),\n    );\n  } else {\n    throw IsarError('Unsupported type for condition');\n  }\n}\n\nvoid _buildConditionStringOp({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required FilterConditionType conditionType,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Object val,\n  required bool include,\n  required bool caseSensitive,\n}) {\n  if (val is Pointer<Char>) {\n    if (val.isNull) {\n      throw IsarError('String operation value must not be null');\n    }\n\n    // ignore: missing_enum_constant_in_switch\n    switch (conditionType) {\n      case FilterConditionType.startsWith:\n        nCall(\n          IC.isar_filter_string_starts_with(\n            colPtr,\n            filterPtr,\n            val,\n            caseSensitive,\n            embeddedColId ?? 0,\n            propertyId!,\n          ),\n        );\n        break;\n      case FilterConditionType.endsWith:\n        nCall(\n          IC.isar_filter_string_ends_with(\n            colPtr,\n            filterPtr,\n            val,\n            caseSensitive,\n            embeddedColId ?? 0,\n            propertyId!,\n          ),\n        );\n        break;\n      case FilterConditionType.contains:\n        nCall(\n          IC.isar_filter_string_contains(\n            colPtr,\n            filterPtr,\n            val,\n            caseSensitive,\n            embeddedColId ?? 0,\n            propertyId!,\n          ),\n        );\n        break;\n      case FilterConditionType.matches:\n        nCall(\n          IC.isar_filter_string_matches(\n            colPtr,\n            filterPtr,\n            val,\n            caseSensitive,\n            embeddedColId ?? 0,\n            propertyId!,\n          ),\n        );\n        break;\n    }\n  } else {\n    throw IsarError('Unsupported type for condition');\n  }\n}\n\nvoid _buildListLength({\n  required Pointer<CIsarCollection> colPtr,\n  required Pointer<Pointer<CFilter>> filterPtr,\n  required int? embeddedColId,\n  required int? propertyId,\n  required Object? lower,\n  required Object? upper,\n}) {\n  if (lower is int && upper is int) {\n    nCall(\n      IC.isar_filter_list_length(\n        colPtr,\n        filterPtr,\n        lower,\n        upper,\n        embeddedColId ?? 0,\n        propertyId!,\n      ),\n    );\n  } else {\n    throw IsarError('Unsupported type for condition');\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/query_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:async';\nimport 'dart:ffi';\nimport 'dart:isolate';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/isar_collection_impl.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/txn.dart';\n\ntypedef QueryDeserialize<T> = List<T> Function(CObjectSet);\n\nclass QueryImpl<T> extends Query<T> implements Finalizable {\n  QueryImpl(this.col, this.queryPtr, this.deserialize, this.propertyId) {\n    NativeFinalizer(isarQueryFree).attach(this, queryPtr.cast());\n  }\n  static const int maxLimit = 4294967295;\n\n  final IsarCollectionImpl<dynamic> col;\n  final Pointer<CQuery> queryPtr;\n  final QueryDeserialize<T> deserialize;\n  final int? propertyId;\n\n  @override\n  Isar get isar => col.isar;\n\n  @override\n  Future<T?> findFirst() {\n    return findInternal(maxLimit).then((List<T> result) {\n      if (result.isNotEmpty) {\n        return result[0];\n      } else {\n        return null;\n      }\n    });\n  }\n\n  @override\n  Future<List<T>> findAll() => findInternal(maxLimit);\n\n  Future<List<T>> findInternal(int limit) {\n    return col.isar.getTxn(false, (Txn txn) async {\n      final resultsPtr = txn.alloc<CObjectSet>();\n      try {\n        IC.isar_q_find(queryPtr, txn.ptr, resultsPtr, limit);\n        await txn.wait();\n        return deserialize(resultsPtr.ref).cast();\n      } finally {\n        IC.isar_free_c_object_set(resultsPtr);\n      }\n    });\n  }\n\n  @override\n  T? findFirstSync() {\n    final results = findSyncInternal(1);\n    if (results.isNotEmpty) {\n      return results[0];\n    } else {\n      return null;\n    }\n  }\n\n  @override\n  List<T> findAllSync() => findSyncInternal(maxLimit);\n\n  List<T> findSyncInternal(int limit) {\n    return col.isar.getTxnSync(false, (Txn txn) {\n      final resultsPtr = txn.getCObjectsSet();\n      try {\n        nCall(IC.isar_q_find(queryPtr, txn.ptr, resultsPtr, limit));\n        return deserialize(resultsPtr.ref).cast();\n      } finally {\n        IC.isar_free_c_object_set(resultsPtr);\n      }\n    });\n  }\n\n  @override\n  Future<bool> deleteFirst() =>\n      deleteInternal(1).then((int count) => count == 1);\n\n  @override\n  Future<int> deleteAll() => deleteInternal(maxLimit);\n\n  Future<int> deleteInternal(int limit) {\n    return col.isar.getTxn(false, (Txn txn) async {\n      final countPtr = txn.alloc<Uint32>();\n      IC.isar_q_delete(queryPtr, col.ptr, txn.ptr, limit, countPtr);\n      await txn.wait();\n      return countPtr.value;\n    });\n  }\n\n  @override\n  bool deleteFirstSync() => deleteSyncInternal(1) == 1;\n\n  @override\n  int deleteAllSync() => deleteSyncInternal(maxLimit);\n\n  int deleteSyncInternal(int limit) {\n    return col.isar.getTxnSync(false, (Txn txn) {\n      final countPtr = txn.alloc<Uint32>();\n      nCall(IC.isar_q_delete(queryPtr, col.ptr, txn.ptr, limit, countPtr));\n      return countPtr.value;\n    });\n  }\n\n  @override\n  Stream<List<T>> watch({bool fireImmediately = false}) {\n    return watchLazy(fireImmediately: fireImmediately)\n        .asyncMap((event) => findAll());\n  }\n\n  @override\n  Stream<void> watchLazy({bool fireImmediately = false}) {\n    final port = ReceivePort();\n    final handle = IC.isar_watch_query(\n      col.isar.ptr,\n      col.ptr,\n      queryPtr,\n      port.sendPort.nativePort,\n    );\n\n    final controller = StreamController<void>(\n      onCancel: () {\n        IC.isar_stop_watching(handle);\n        port.close();\n      },\n    );\n\n    if (fireImmediately) {\n      controller.add(null);\n    }\n\n    controller.addStream(port);\n    return controller.stream;\n  }\n\n  @override\n  Future<R> exportJsonRaw<R>(R Function(Uint8List) callback) {\n    return col.isar.getTxn(false, (Txn txn) async {\n      final bytesPtrPtr = txn.alloc<Pointer<Uint8>>();\n      final lengthPtr = txn.alloc<Uint32>();\n      final idNamePtr = col.schema.idName.toCString(txn.alloc);\n\n      nCall(\n        IC.isar_q_export_json(\n          queryPtr,\n          col.ptr,\n          txn.ptr,\n          idNamePtr,\n          bytesPtrPtr,\n          lengthPtr,\n        ),\n      );\n\n      try {\n        await txn.wait();\n        final bytes = bytesPtrPtr.value.asTypedList(lengthPtr.value);\n        return callback(bytes);\n      } finally {\n        IC.isar_free_json(bytesPtrPtr.value, lengthPtr.value);\n      }\n    });\n  }\n\n  @override\n  R exportJsonRawSync<R>(R Function(Uint8List) callback) {\n    return col.isar.getTxnSync(false, (Txn txn) {\n      final bytesPtrPtr = txn.alloc<Pointer<Uint8>>();\n      final lengthPtr = txn.alloc<Uint32>();\n      final idNamePtr = col.schema.idName.toCString(txn.alloc);\n\n      try {\n        nCall(\n          IC.isar_q_export_json(\n            queryPtr,\n            col.ptr,\n            txn.ptr,\n            idNamePtr,\n            bytesPtrPtr,\n            lengthPtr,\n          ),\n        );\n        final bytes = bytesPtrPtr.value.asTypedList(lengthPtr.value);\n        return callback(bytes);\n      } finally {\n        IC.isar_free_json(bytesPtrPtr.value, lengthPtr.value);\n      }\n    });\n  }\n\n  @override\n  Future<R?> aggregate<R>(AggregationOp op) async {\n    return col.isar.getTxn(false, (Txn txn) async {\n      final resultPtrPtr = txn.alloc<Pointer<CAggregationResult>>();\n\n      IC.isar_q_aggregate(\n        col.ptr,\n        queryPtr,\n        txn.ptr,\n        op.index,\n        propertyId ?? 0,\n        resultPtrPtr,\n      );\n      await txn.wait();\n\n      return _convertAggregatedResult<R>(resultPtrPtr.value, op);\n    });\n  }\n\n  @override\n  R? aggregateSync<R>(AggregationOp op) {\n    return col.isar.getTxnSync(false, (Txn txn) {\n      final resultPtrPtr = txn.alloc<Pointer<CAggregationResult>>();\n\n      nCall(\n        IC.isar_q_aggregate(\n          col.ptr,\n          queryPtr,\n          txn.ptr,\n          op.index,\n          propertyId ?? 0,\n          resultPtrPtr,\n        ),\n      );\n      return _convertAggregatedResult(resultPtrPtr.value, op);\n    });\n  }\n\n  R? _convertAggregatedResult<R>(\n    Pointer<CAggregationResult> resultPtr,\n    AggregationOp op,\n  ) {\n    final nullable = op == AggregationOp.min || op == AggregationOp.max;\n    if (R == int || R == DateTime) {\n      final value = IC.isar_q_aggregate_long_result(resultPtr);\n      if (nullable && value == nullLong) {\n        return null;\n      }\n      if (R == int) {\n        return value as R;\n      } else {\n        return DateTime.fromMicrosecondsSinceEpoch(value, isUtc: true).toLocal()\n            as R;\n      }\n    } else {\n      final value = IC.isar_q_aggregate_double_result(resultPtr);\n      if (nullable && value.isNaN) {\n        return null;\n      } else {\n        return value as R;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/split_words.dart",
    "content": "import 'dart:ffi';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/src/native/encode_string.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/isar_reader_impl.dart';\n\n// ignore: public_member_api_docs\nList<String> isarSplitWords(String input) {\n  initializeCoreBinary();\n\n  final bytesPtr = malloc<Uint8>(input.length * 3);\n  final bytes = bytesPtr.asTypedList(input.length * 3);\n  final byteCount = encodeString(input, bytes, 0);\n\n  final wordCountPtr = malloc<Uint32>();\n  final boundariesPtr =\n      IC.isar_find_word_boundaries(bytesPtr.cast(), byteCount, wordCountPtr);\n  final wordCount = wordCountPtr.value;\n  final boundaries = boundariesPtr.asTypedList(wordCount * 2);\n\n  final words = <String>[];\n  for (var i = 0; i < wordCount * 2; i++) {\n    final wordBytes = bytes.sublist(boundaries[i++], boundaries[i]);\n    words.add(IsarReaderImpl.utf8Decoder.convert(wordBytes));\n  }\n\n  IC.isar_free_word_boundaries(boundariesPtr, wordCount);\n  malloc.free(bytesPtr);\n  malloc.free(wordCountPtr);\n\n  return words;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/native/txn.dart",
    "content": "import 'dart:async';\nimport 'dart:collection';\nimport 'dart:ffi';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/common/isar_common.dart';\nimport 'package:isar/src/native/bindings.dart';\nimport 'package:isar/src/native/isar_core.dart';\n\n/// @nodoc\nclass Txn extends Transaction {\n  /// @nodoc\n  Txn.sync(Isar isar, this.ptr, bool write) : super(isar, true, write);\n\n  /// @nodoc\n  Txn.async(Isar isar, this.ptr, bool write, Stream<void> stream)\n      : super(isar, false, write) {\n    _completers = Queue();\n    _portSubscription = stream.listen(\n      (_) => _completers.removeFirst().complete(),\n      onError: (Object e) => _completers.removeFirst().completeError(e),\n    );\n  }\n\n  @override\n  bool active = true;\n\n  /// An arena allocator that has the same lifetime as this transaction.\n  final alloc = Arena(malloc);\n\n  /// The pointer to the native transaction.\n  final Pointer<CIsarTxn> ptr;\n  Pointer<CObject>? _cObjPtr;\n  Pointer<CObjectSet>? _cObjSetPtr;\n\n  late Pointer<Uint8> _buffer;\n  int _bufferLen = -1;\n\n  late final Queue<Completer<void>> _completers;\n  late final StreamSubscription<void>? _portSubscription;\n\n  /// Get a shared CObject pointer\n  Pointer<CObject> getCObject() {\n    _cObjPtr ??= alloc<CObject>();\n    return _cObjPtr!;\n  }\n\n  /// Get a shared CObjectSet pointer\n  Pointer<CObjectSet> getCObjectsSet() {\n    _cObjSetPtr ??= alloc();\n    return _cObjSetPtr!;\n  }\n\n  /// Allocate a new CObjectSet with the given capacity.\n  Pointer<CObjectSet> newCObjectSet(int length) {\n    final cObjSetPtr = alloc<CObjectSet>();\n    cObjSetPtr.ref\n      ..objects = alloc<CObject>(length)\n      ..length = length;\n    return cObjSetPtr;\n  }\n\n  /// Get a shared buffer with at least the specified size.\n  Pointer<Uint8> getBuffer(int size) {\n    if (_bufferLen < size) {\n      final allocSize = (size * 1.3).toInt();\n      _buffer = alloc(allocSize);\n      _bufferLen = allocSize;\n    }\n    return _buffer;\n  }\n\n  /// Wait for the latest async operation to complete.\n  Future<void> wait() {\n    final completer = Completer<void>();\n    _completers.add(completer);\n    return completer.future;\n  }\n\n  @override\n  Future<void> commit() async {\n    active = false;\n    IC.isar_txn_finish(ptr, true);\n    await wait();\n    unawaited(_portSubscription!.cancel());\n  }\n\n  @override\n  void commitSync() {\n    active = false;\n    nCall(IC.isar_txn_finish(ptr, true));\n  }\n\n  @override\n  Future<void> abort() async {\n    active = false;\n    IC.isar_txn_finish(ptr, false);\n    await wait();\n    unawaited(_portSubscription!.cancel());\n  }\n\n  @override\n  void abortSync() {\n    active = false;\n    nCall(IC.isar_txn_finish(ptr, false));\n  }\n\n  @override\n  void free() {\n    alloc.releaseAll();\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/query.dart",
    "content": "part of isar;\n\n/// Querying is how you find records that match certain conditions.\nabstract class Query<T> {\n  /// The default precision for floating point number queries.\n  static const double epsilon = 0.00001;\n\n  /// The corresponding Isar instance.\n  Isar get isar;\n\n  /// {@template query_find_first}\n  /// Find the first object that matches this query or `null` if no object\n  /// matches.\n  /// {@endtemplate}\n  Future<T?> findFirst();\n\n  /// {@macro query_find_first}\n  T? findFirstSync();\n\n  /// {@template query_find_all}\n  /// Find all objects that match this query.\n  /// {@endtemplate}\n  Future<List<T>> findAll();\n\n  /// {@macro query_find_all}\n  List<T> findAllSync();\n\n  /// @nodoc\n  @protected\n  Future<R?> aggregate<R>(AggregationOp op);\n\n  /// @nodoc\n  @protected\n  R? aggregateSync<R>(AggregationOp op);\n\n  /// {@template query_count}\n  /// Count how many objects match this query.\n  ///\n  /// This operation is much faster than using `findAll().length`.\n  /// {@endtemplate}\n  Future<int> count() =>\n      aggregate<int>(AggregationOp.count).then((int? value) => value!);\n\n  /// {@macro query_count}\n  int countSync() => aggregateSync<int>(AggregationOp.count)!;\n\n  /// {@template query_is_empty}\n  /// Returns `true` if there are no objects that match this query.\n  ///\n  /// This operation is faster than using `count() == 0`.\n  /// {@endtemplate}\n  Future<bool> isEmpty() =>\n      aggregate<int>(AggregationOp.isEmpty).then((value) => value == 1);\n\n  /// {@macro query_is_empty}\n  bool isEmptySync() => aggregateSync<int>(AggregationOp.isEmpty) == 1;\n\n  /// {@template query_is_not_empty}\n  /// Returns `true` if there are objects that match this query.\n  ///\n  /// This operation is faster than using `count() > 0`.\n  /// {@endtemplate}\n  Future<bool> isNotEmpty() =>\n      aggregate<int>(AggregationOp.isEmpty).then((value) => value == 0);\n\n  /// {@macro query_is_not_empty}\n  bool isNotEmptySync() => aggregateSync<int>(AggregationOp.isEmpty) == 0;\n\n  /// {@template query_delete_first}\n  /// Delete the first object that matches this query. Returns whether a object\n  /// has been deleted.\n  /// {@endtemplate}\n  Future<bool> deleteFirst();\n\n  /// {@macro query_delete_first}\n  bool deleteFirstSync();\n\n  /// {@template query_delete_all}\n  /// Delete all objects that match this query. Returns the number of deleted\n  /// objects.\n  /// {@endtemplate}\n  Future<int> deleteAll();\n\n  /// {@macro query_delete_all}\n  int deleteAllSync();\n\n  /// {@template query_watch}\n  /// Create a watcher that yields the results of this query whenever its\n  /// results have (potentially) changed.\n  ///\n  /// If you don't always use the results, consider using `watchLazy` and rerun\n  /// the query manually. If [fireImmediately] is `true`, the results will be\n  /// sent to the consumer immediately.\n  /// {@endtemplate}\n  Stream<List<T>> watch({bool fireImmediately = false});\n\n  /// {@template query_watch_lazy}\n  /// Watch the query for changes. If [fireImmediately] is `true`, an event will\n  /// be fired immediately.\n  /// {@endtemplate}\n  Stream<void> watchLazy({bool fireImmediately = false});\n\n  /// {@template query_export_json_raw}\n  /// Export the results of this query as json bytes.\n  ///\n  /// **IMPORTANT:** Do not leak the bytes outside the callback. If you need to\n  /// use the bytes outside, create a copy of the `Uint8List`.\n  /// {@endtemplate}\n  Future<R> exportJsonRaw<R>(R Function(Uint8List) callback);\n\n  /// {@macro query_export_json_raw}\n  R exportJsonRawSync<R>(R Function(Uint8List) callback);\n\n  /// {@template query_export_json}\n  /// Export the results of this query as json.\n  /// {@endtemplate}\n  Future<List<Map<String, dynamic>>> exportJson() {\n    return exportJsonRaw((Uint8List bytes) {\n      final json = jsonDecode(const Utf8Decoder().convert(bytes)) as List;\n      return json.cast<Map<String, dynamic>>();\n    });\n  }\n\n  /// {@macro query_export_json}\n  List<Map<String, dynamic>> exportJsonSync() {\n    return exportJsonRawSync((Uint8List bytes) {\n      final json = jsonDecode(const Utf8Decoder().convert(bytes)) as List;\n      return json.cast<Map<String, dynamic>>();\n    });\n  }\n}\n\n/// @nodoc\n@protected\nenum AggregationOp {\n  /// Finds the smallest value.\n  min,\n\n  /// Finds the largest value.\n  max,\n\n  /// Calculates the sum of all values.\n  sum,\n\n  /// Calculates the average of all values.\n  average,\n\n  /// Counts all values.\n  count,\n\n  /// Returns `true` if the query has no results.\n  isEmpty,\n}\n\n/// Extension for Queries\nextension QueryAggregation<T extends num> on Query<T?> {\n  /// {@template aggregation_min}\n  /// Returns the minimum value of this query.\n  /// {@endtemplate}\n  Future<T?> min() => aggregate<T>(AggregationOp.min);\n\n  /// {@macro aggregation_min}\n  T? minSync() => aggregateSync<T>(AggregationOp.min);\n\n  /// {@template aggregation_max}\n  /// Returns the maximum value of this query.\n  /// {@endtemplate}\n  Future<T?> max() => aggregate<T>(AggregationOp.max);\n\n  /// {@macro aggregation_max}\n  T? maxSync() => aggregateSync<T>(AggregationOp.max);\n\n  /// {@template aggregation_average}\n  /// Returns the average value of this query.\n  /// {@endtemplate}\n  Future<double> average() =>\n      aggregate<double>(AggregationOp.average).then((double? value) => value!);\n\n  /// {@macro aggregation_average}\n  double averageSync() => aggregateSync<double>(AggregationOp.average)!;\n\n  /// {@template aggregation_sum}\n  /// Returns the sum of all values of this query.\n  /// {@endtemplate}\n  Future<T> sum() => aggregate<T>(AggregationOp.sum).then((value) => value!);\n\n  /// {@macro aggregation_sum}\n  T sumSync() => aggregateSync<T>(AggregationOp.sum)!;\n}\n\n/// Extension for Queries\nextension QueryDateAggregation<T extends DateTime?> on Query<T> {\n  /// {@macro aggregation_min}\n  Future<DateTime?> min() => aggregate<DateTime>(AggregationOp.min);\n\n  /// {@macro aggregation_min}\n  DateTime? minSync() => aggregateSync<DateTime>(AggregationOp.min);\n\n  /// {@macro aggregation_max}\n  Future<DateTime?> max() => aggregate<DateTime>(AggregationOp.max);\n\n  /// {@macro aggregation_max}\n  DateTime? maxSync() => aggregateSync<DateTime>(AggregationOp.max);\n}\n"
  },
  {
    "path": "packages/isar/lib/src/query_builder.dart",
    "content": "part of isar;\n\n/// @nodoc\n@protected\ntypedef FilterQuery<OBJ> = QueryBuilder<OBJ, OBJ, QAfterFilterCondition>\n    Function(QueryBuilder<OBJ, OBJ, QFilterCondition> q);\n\n/// Query builders are used to create queries in a safe way.\n///\n/// Acquire a `QueryBuilder` instance using `collection.where()` or\n/// `collection.filter()`.\nclass QueryBuilder<OBJ, R, S> {\n  /// @nodoc\n  @protected\n  const QueryBuilder(this._query);\n\n  final QueryBuilderInternal<OBJ> _query;\n\n  /// @nodoc\n  @protected\n  static QueryBuilder<OBJ, R, S> apply<OBJ, R, S>(\n    QueryBuilder<OBJ, dynamic, dynamic> qb,\n    QueryBuilderInternal<OBJ> Function(QueryBuilderInternal<OBJ> query)\n        transform,\n  ) {\n    return QueryBuilder(transform(qb._query));\n  }\n}\n\n/// @nodoc\n@protected\nclass QueryBuilderInternal<OBJ> {\n  /// @nodoc\n  const QueryBuilderInternal({\n    this.collection,\n    this.whereClauses = const [],\n    this.whereDistinct = false,\n    this.whereSort = Sort.asc,\n    this.filter = const FilterGroup.and([]),\n    this.filterGroupType = FilterGroupType.and,\n    this.filterNot = false,\n    this.distinctByProperties = const [],\n    this.sortByProperties = const [],\n    this.offset,\n    this.limit,\n    this.propertyName,\n  });\n\n  /// @nodoc\n  final IsarCollection<OBJ>? collection;\n\n  /// @nodoc\n  final List<WhereClause> whereClauses;\n\n  /// @nodoc\n  final bool whereDistinct;\n\n  /// @nodoc\n  final Sort whereSort;\n\n  /// @nodoc\n  final FilterGroup filter;\n\n  /// @nodoc\n  final FilterGroupType filterGroupType;\n\n  /// @nodoc\n  final bool filterNot;\n\n  /// @nodoc\n  final List<DistinctProperty> distinctByProperties;\n\n  /// @nodoc\n  final List<SortProperty> sortByProperties;\n\n  /// @nodoc\n  final int? offset;\n\n  /// @nodoc\n  final int? limit;\n\n  /// @nodoc\n  final String? propertyName;\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> addFilterCondition(FilterOperation cond) {\n    if (filterNot) {\n      cond = FilterGroup.not(cond);\n    }\n\n    late FilterGroup filterGroup;\n\n    if (filter.type == filterGroupType || filter.filters.length <= 1) {\n      filterGroup = FilterGroup(\n        type: filterGroupType,\n        filters: [...filter.filters, cond],\n      );\n    } else if (filterGroupType == FilterGroupType.and) {\n      filterGroup = FilterGroup(\n        type: filter.type,\n        filters: [\n          ...filter.filters.sublist(0, filter.filters.length - 1),\n          FilterGroup(\n            type: filterGroupType,\n            filters: [filter.filters.last, cond],\n          ),\n        ],\n      );\n    } else {\n      filterGroup = FilterGroup(\n        type: filterGroupType,\n        filters: [filter, cond],\n      );\n    }\n\n    return copyWith(\n      filter: filterGroup,\n      filterGroupType: FilterGroupType.and,\n      filterNot: false,\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> addWhereClause(WhereClause where) {\n    return copyWith(whereClauses: [...whereClauses, where]);\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> group(FilterQuery<OBJ> q) {\n    // ignore: prefer_const_constructors\n    final qb = q(QueryBuilder(QueryBuilderInternal()));\n    return addFilterCondition(qb._query.filter);\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> listLength<E>(\n    String property,\n    int lower,\n    bool includeLower,\n    int upper,\n    bool includeUpper,\n  ) {\n    if (!includeLower) {\n      lower += 1;\n    }\n    if (!includeUpper) {\n      if (upper == 0) {\n        lower = 1;\n      } else {\n        upper -= 1;\n      }\n    }\n    return addFilterCondition(\n      FilterCondition.listLength(\n        property: property,\n        lower: lower,\n        upper: upper,\n      ),\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> object<E>(\n    FilterQuery<E> q,\n    String property,\n  ) {\n    // ignore: prefer_const_constructors\n    final qb = q(QueryBuilder(QueryBuilderInternal()));\n    return addFilterCondition(\n      ObjectFilter(filter: qb._query.filter, property: property),\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> link<E>(\n    FilterQuery<E> q,\n    String linkName,\n  ) {\n    // ignore: prefer_const_constructors\n    final qb = q(QueryBuilder(QueryBuilderInternal()));\n    return addFilterCondition(\n      LinkFilter(filter: qb._query.filter, linkName: linkName),\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> linkLength<E>(\n    String linkName,\n    int lower,\n    bool includeLower,\n    int upper,\n    bool includeUpper,\n  ) {\n    if (!includeLower) {\n      lower += 1;\n    }\n    if (!includeUpper) {\n      if (upper == 0) {\n        lower = 1;\n      } else {\n        upper -= 1;\n      }\n    }\n    return addFilterCondition(\n      LinkFilter.length(\n        lower: lower,\n        upper: upper,\n        linkName: linkName,\n      ),\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> addSortBy(String propertyName, Sort sort) {\n    return copyWith(\n      sortByProperties: [\n        ...sortByProperties,\n        SortProperty(property: propertyName, sort: sort),\n      ],\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> addDistinctBy(\n    String propertyName, {\n    bool? caseSensitive,\n  }) {\n    return copyWith(\n      distinctByProperties: [\n        ...distinctByProperties,\n        DistinctProperty(\n          property: propertyName,\n          caseSensitive: caseSensitive,\n        ),\n      ],\n    );\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> addPropertyName<E>(String propertyName) {\n    return copyWith(propertyName: propertyName);\n  }\n\n  /// @nodoc\n  QueryBuilderInternal<OBJ> copyWith({\n    List<WhereClause>? whereClauses,\n    FilterGroup? filter,\n    bool? filterIsGrouped,\n    FilterGroupType? filterGroupType,\n    bool? filterNot,\n    List<FilterGroup>? parentFilters,\n    List<DistinctProperty>? distinctByProperties,\n    List<SortProperty>? sortByProperties,\n    int? offset,\n    int? limit,\n    String? propertyName,\n  }) {\n    assert(offset == null || offset >= 0, 'Invalid offset');\n    assert(limit == null || limit >= 0, 'Invalid limit');\n    return QueryBuilderInternal(\n      collection: collection,\n      whereClauses: whereClauses ?? List.unmodifiable(this.whereClauses),\n      whereDistinct: whereDistinct,\n      whereSort: whereSort,\n      filter: filter ?? this.filter,\n      filterGroupType: filterGroupType ?? this.filterGroupType,\n      filterNot: filterNot ?? this.filterNot,\n      distinctByProperties:\n          distinctByProperties ?? List.unmodifiable(this.distinctByProperties),\n      sortByProperties:\n          sortByProperties ?? List.unmodifiable(this.sortByProperties),\n      offset: offset ?? this.offset,\n      limit: limit ?? this.limit,\n      propertyName: propertyName ?? this.propertyName,\n    );\n  }\n\n  /// @nodoc\n  @protected\n  Query<R> build<R>() {\n    return collection!.buildQuery(\n      whereDistinct: whereDistinct,\n      whereSort: whereSort,\n      whereClauses: whereClauses,\n      filter: filter,\n      sortBy: sortByProperties,\n      distinctBy: distinctByProperties,\n      offset: offset,\n      limit: limit,\n      property: propertyName,\n    );\n  }\n}\n\n/// @nodoc\n///\n/// Right after query starts\n@protected\nclass QWhere\n    implements\n        QWhereClause,\n        QFilter,\n        QSortBy,\n        QDistinct,\n        QOffset,\n        QLimit,\n        QQueryProperty {}\n\n/// @nodoc\n///\n/// No more where conditions are allowed\n@protected\nclass QAfterWhere\n    implements QFilter, QSortBy, QDistinct, QOffset, QLimit, QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QWhereClause {}\n\n/// @nodoc\n@protected\nclass QAfterWhereClause\n    implements\n        QWhereOr,\n        QFilter,\n        QSortBy,\n        QDistinct,\n        QOffset,\n        QLimit,\n        QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QWhereOr {}\n\n/// @nodoc\n@protected\nclass QFilter {}\n\n/// @nodoc\n@protected\nclass QFilterCondition {}\n\n/// @nodoc\n@protected\nclass QAfterFilterCondition\n    implements\n        QFilterCondition,\n        QFilterOperator,\n        QSortBy,\n        QDistinct,\n        QOffset,\n        QLimit,\n        QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QFilterOperator {}\n\n/// @nodoc\n@protected\nclass QAfterFilterOperator implements QFilterCondition {}\n\n/// @nodoc\n@protected\nclass QSortBy {}\n\n/// @nodoc\n@protected\nclass QAfterSortBy\n    implements QSortThenBy, QDistinct, QOffset, QLimit, QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QSortThenBy {}\n\n/// @nodoc\n@protected\nclass QDistinct implements QOffset, QLimit, QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QOffset {}\n\n/// @nodoc\n@protected\nclass QAfterOffset implements QLimit, QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QLimit {}\n\n/// @nodoc\n@protected\nclass QAfterLimit implements QQueryProperty {}\n\n/// @nodoc\n@protected\nclass QQueryProperty implements QQueryOperations {}\n\n/// @nodoc\n@protected\nclass QQueryOperations {}\n"
  },
  {
    "path": "packages/isar/lib/src/query_builder_extensions.dart",
    "content": "part of isar;\n\n/// Extension for QueryBuilders.\nextension QueryWhereOr<OBJ, R> on QueryBuilder<OBJ, R, QWhereOr> {\n  /// Union of two where clauses.\n  QueryBuilder<OBJ, R, QWhereClause> or() {\n    return QueryBuilder(_query);\n  }\n}\n\n/// @nodoc\n@protected\ntypedef WhereRepeatModifier<OBJ, R, E> = QueryBuilder<OBJ, R, QAfterWhereClause>\n    Function(QueryBuilder<OBJ, R, QWhereClause> q, E element);\n\n/// Extension for QueryBuilders.\nextension QueryWhere<OBJ, R> on QueryBuilder<OBJ, R, QWhereClause> {\n  /// Joins the results of the [modifier] for each item in [items] using logical\n  /// OR. So an object will be included if it matches at least one of the\n  /// resulting where clauses.\n  ///\n  /// If [items] is empty, this is a no-op.\n  QueryBuilder<OBJ, R, QAfterWhereClause> anyOf<E, RS>(\n    Iterable<E> items,\n    WhereRepeatModifier<OBJ, R, E> modifier,\n  ) {\n    QueryBuilder<OBJ, R, QAfterWhereClause>? q;\n    for (final e in items) {\n      q = modifier(q?.or() ?? QueryBuilder(_query), e);\n    }\n    return q ?? QueryBuilder(_query);\n  }\n}\n\n/// Extension for QueryBuilders.\nextension QueryFilters<OBJ, R> on QueryBuilder<OBJ, R, QFilter> {\n  /// Start using filter conditions.\n  QueryBuilder<OBJ, R, QFilterCondition> filter() {\n    return QueryBuilder(_query);\n  }\n}\n\n/// @nodoc\n@protected\ntypedef FilterRepeatModifier<OBJ, R, E>\n    = QueryBuilder<OBJ, R, QAfterFilterCondition> Function(\n  QueryBuilder<OBJ, R, QFilterCondition> q,\n  E element,\n);\n\n/// Extension for QueryBuilders.\nextension QueryFilterAndOr<OBJ, R> on QueryBuilder<OBJ, R, QFilterOperator> {\n  /// Intersection of two filter conditions.\n  QueryBuilder<OBJ, R, QFilterCondition> and() {\n    return QueryBuilder.apply(\n      this,\n      (q) => q.copyWith(filterGroupType: FilterGroupType.and),\n    );\n  }\n\n  /// Union of two filter conditions.\n  QueryBuilder<OBJ, R, QFilterCondition> or() {\n    return QueryBuilder.apply(\n      this,\n      (q) => q.copyWith(filterGroupType: FilterGroupType.or),\n    );\n  }\n\n  /// Logical XOR of two filter conditions.\n  QueryBuilder<OBJ, R, QFilterCondition> xor() {\n    return QueryBuilder.apply(\n      this,\n      (q) => q.copyWith(filterGroupType: FilterGroupType.xor),\n    );\n  }\n}\n\n/// Extension for QueryBuilders.\nextension QueryFilterNot<OBJ, R> on QueryBuilder<OBJ, R, QFilterCondition> {\n  /// Complement the next filter condition or group.\n  QueryBuilder<OBJ, R, QFilterCondition> not() {\n    return QueryBuilder.apply(\n      this,\n      (q) => q.copyWith(filterNot: !q.filterNot),\n    );\n  }\n\n  /// Joins the results of the [modifier] for each item in [items] using logical\n  /// OR. So an object will be included if it matches at least one of the\n  /// resulting filters.\n  ///\n  /// If [items] is empty, this is a no-op.\n  QueryBuilder<OBJ, R, QAfterFilterCondition> anyOf<E, RS>(\n    Iterable<E> items,\n    FilterRepeatModifier<OBJ, OBJ, E> modifier,\n  ) {\n    return QueryBuilder.apply(this, (query) {\n      return query.group((q) {\n        var q2 = QueryBuilder<OBJ, OBJ, QAfterFilterCondition>(q._query);\n        for (final e in items) {\n          q2 = modifier(q2.or(), e);\n        }\n        return q2;\n      });\n    });\n  }\n\n  /// Joins the results of the [modifier] for each item in [items] using logical\n  /// AND. So an object will be included if it matches all of the resulting\n  /// filters.\n  ///\n  /// If [items] is empty, this is a no-op.\n  QueryBuilder<OBJ, R, QAfterFilterCondition> allOf<E, RS>(\n    Iterable<E> items,\n    FilterRepeatModifier<OBJ, OBJ, E> modifier,\n  ) {\n    return QueryBuilder.apply(this, (query) {\n      return query.group((q) {\n        var q2 = QueryBuilder<OBJ, OBJ, QAfterFilterCondition>(q._query);\n        for (final e in items) {\n          q2 = modifier(q2.and(), e);\n        }\n        return q2;\n      });\n    });\n  }\n\n  /// Joins the results of the [modifier] for each item in [items] using logical\n  /// XOR. So an object will be included if it matches exactly one of the\n  /// resulting filters.\n  ///\n  /// If [items] is empty, this is a no-op.\n  QueryBuilder<OBJ, R, QAfterFilterCondition> oneOf<E, RS>(\n    Iterable<E> items,\n    FilterRepeatModifier<OBJ, R, E> modifier,\n  ) {\n    QueryBuilder<OBJ, R, QAfterFilterCondition>? q;\n    for (final e in items) {\n      q = modifier(q?.xor() ?? QueryBuilder(_query), e);\n    }\n    return q ?? QueryBuilder(_query);\n  }\n}\n\n/// Extension for QueryBuilders.\nextension QueryFilterNoGroups<OBJ, R>\n    on QueryBuilder<OBJ, R, QFilterCondition> {\n  /// Group filter conditions.\n  QueryBuilder<OBJ, R, QAfterFilterCondition> group(FilterQuery<OBJ> q) {\n    return QueryBuilder.apply(this, (query) => query.group(q));\n  }\n}\n\n/// Extension for QueryBuilders.\nextension QueryOffset<OBJ, R> on QueryBuilder<OBJ, R, QOffset> {\n  /// Offset the query results by a static number.\n  QueryBuilder<OBJ, R, QAfterOffset> offset(int offset) {\n    return QueryBuilder.apply(this, (q) => q.copyWith(offset: offset));\n  }\n}\n\n/// Extension for QueryBuilders.\nextension QueryLimit<OBJ, R> on QueryBuilder<OBJ, R, QLimit> {\n  /// Limit the maximum number of query results.\n  QueryBuilder<OBJ, R, QAfterLimit> limit(int limit) {\n    return QueryBuilder.apply(this, (q) => q.copyWith(limit: limit));\n  }\n}\n\n/// @nodoc\n@protected\ntypedef QueryOption<OBJ, S, RS> = QueryBuilder<OBJ, OBJ, RS> Function(\n  QueryBuilder<OBJ, OBJ, S> q,\n);\n\n/// Extension for QueryBuilders.\nextension QueryModifier<OBJ, S> on QueryBuilder<OBJ, OBJ, S> {\n  /// Only apply a part of the query if `enabled` is true.\n  QueryBuilder<OBJ, OBJ, RS> optional<RS>(\n    bool enabled,\n    QueryOption<OBJ, S, RS> option,\n  ) {\n    if (enabled) {\n      return option(this);\n    } else {\n      return QueryBuilder(_query);\n    }\n  }\n}\n\n/// Extension for QueryBuilders\nextension QueryExecute<OBJ, R> on QueryBuilder<OBJ, R, QQueryOperations> {\n  /// Create a query from this query builder.\n  Query<R> build() => _query.build();\n\n  /// {@macro query_find_first}\n  Future<R?> findFirst() => build().findFirst();\n\n  /// {@macro query_find_first}\n  R? findFirstSync() => build().findFirstSync();\n\n  /// {@macro query_find_all}\n  Future<List<R>> findAll() => build().findAll();\n\n  /// {@macro query_find_all}\n  List<R> findAllSync() => build().findAllSync();\n\n  /// {@macro query_count}\n  Future<int> count() => build().count();\n\n  /// {@macro query_count}\n  int countSync() => build().countSync();\n\n  /// {@macro query_is_empty}\n  Future<bool> isEmpty() => build().isEmpty();\n\n  /// {@macro query_is_empty}\n  bool isEmptySync() => build().isEmptySync();\n\n  /// {@macro query_is_not_empty}\n  Future<bool> isNotEmpty() => build().isNotEmpty();\n\n  /// {@macro query_is_not_empty}\n  bool isNotEmptySync() => build().isNotEmptySync();\n\n  /// {@macro query_delete_first}\n  Future<bool> deleteFirst() => build().deleteFirst();\n\n  /// {@macro query_delete_first}\n  bool deleteFirstSync() => build().deleteFirstSync();\n\n  /// {@macro query_delete_all}\n  Future<int> deleteAll() => build().deleteAll();\n\n  /// {@macro query_delete_all}\n  int deleteAllSync() => build().deleteAllSync();\n\n  /// {@macro query_watch}\n  Stream<List<R>> watch({bool fireImmediately = false}) =>\n      build().watch(fireImmediately: fireImmediately);\n\n  /// {@macro query_watch_lazy}\n  Stream<void> watchLazy({bool fireImmediately = false}) =>\n      build().watchLazy(fireImmediately: fireImmediately);\n\n  /// {@macro query_export_json_raw}\n  Future<T> exportJsonRaw<T>(T Function(Uint8List) callback) =>\n      build().exportJsonRaw(callback);\n\n  /// {@macro query_export_json_raw}\n  T exportJsonRawSync<T>(T Function(Uint8List) callback) =>\n      build().exportJsonRawSync(callback);\n\n  /// {@macro query_export_json}\n  Future<List<Map<String, dynamic>>> exportJson() => build().exportJson();\n\n  /// {@macro query_export_json}\n  List<Map<String, dynamic>> exportJsonSync() => build().exportJsonSync();\n}\n\n/// Extension for QueryBuilders\nextension QueryExecuteAggregation<OBJ, T extends num>\n    on QueryBuilder<OBJ, T?, QQueryOperations> {\n  /// {@macro aggregation_min}\n  Future<T?> min() => build().min();\n\n  /// {@macro aggregation_min}\n  T? minSync() => build().minSync();\n\n  /// {@macro aggregation_max}\n  Future<T?> max() => build().max();\n\n  /// {@macro aggregation_max}\n  T? maxSync() => build().maxSync();\n\n  /// {@macro aggregation_average}\n  Future<double> average() => build().average();\n\n  /// {@macro aggregation_average}\n  double averageSync() => build().averageSync();\n\n  /// {@macro aggregation_sum}\n  Future<T> sum() => build().sum();\n\n  /// {@macro aggregation_sum}\n  T sumSync() => build().sumSync();\n}\n\n/// Extension for QueryBuilders\nextension QueryExecuteDateAggregation<OBJ>\n    on QueryBuilder<OBJ, DateTime?, QQueryOperations> {\n  /// {@macro aggregation_min}\n  Future<DateTime?> min() => build().min();\n\n  /// {@macro aggregation_min}\n  DateTime? minSync() => build().minSync();\n\n  /// {@macro aggregation_max}\n  Future<DateTime?> max() => build().max();\n\n  /// {@macro aggregation_max}\n  DateTime? maxSync() => build().maxSync();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/query_components.dart",
    "content": "part of isar;\n\n/// A where clause to traverse an Isar index.\nabstract class WhereClause {\n  const WhereClause._();\n}\n\n/// A where clause traversing the primary index (ids).\nclass IdWhereClause extends WhereClause {\n  /// Where clause that matches all ids. Useful to get sorted results.\n  const IdWhereClause.any()\n      : lower = null,\n        upper = null,\n        includeLower = true,\n        includeUpper = true,\n        super._();\n\n  /// Where clause that matches all id values greater than the given [lower]\n  /// bound.\n  const IdWhereClause.greaterThan({\n    required Id this.lower,\n    this.includeLower = true,\n  })  : upper = null,\n        includeUpper = true,\n        super._();\n\n  /// Where clause that matches all id values less than the given [upper]\n  /// bound.\n  const IdWhereClause.lessThan({\n    required Id this.upper,\n    this.includeUpper = true,\n  })  : lower = null,\n        includeLower = true,\n        super._();\n\n  /// Where clause that matches the id value equal to the given [value].\n  const IdWhereClause.equalTo({\n    required Id value,\n  })  : lower = value,\n        upper = value,\n        includeLower = true,\n        includeUpper = true,\n        super._();\n\n  /// Where clause that matches all id values between the given [lower] and\n  /// [upper] bounds.\n  const IdWhereClause.between({\n    this.lower,\n    this.includeLower = true,\n    this.upper,\n    this.includeUpper = true,\n  }) : super._();\n\n  /// The lower bound id or `null` for unbounded.\n  final Id? lower;\n\n  /// Whether the lower bound should be included in the results.\n  final bool includeLower;\n\n  /// The upper bound id or `null` for unbounded.\n  final Id? upper;\n\n  /// Whether the upper bound should be included in the results.\n  final bool includeUpper;\n}\n\n/// A where clause traversing an index.\nclass IndexWhereClause extends WhereClause {\n  /// Where clause that matches all index values. Useful to get sorted results.\n  const IndexWhereClause.any({required this.indexName})\n      : lower = null,\n        upper = null,\n        includeLower = true,\n        includeUpper = true,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Where clause that matches all index values greater than the given [lower]\n  /// bound.\n  ///\n  /// For composite indexes, the first elements of the [lower] list are checked\n  /// for equality.\n  const IndexWhereClause.greaterThan({\n    required this.indexName,\n    required IndexKey this.lower,\n    this.includeLower = true,\n    this.epsilon = Query.epsilon,\n  })  : upper = null,\n        includeUpper = true,\n        super._();\n\n  /// Where clause that matches all index values less than the given [upper]\n  /// bound.\n  ///\n  /// For composite indexes, the first elements of the [upper] list are checked\n  /// for equality.\n  const IndexWhereClause.lessThan({\n    required this.indexName,\n    required IndexKey this.upper,\n    this.includeUpper = true,\n    this.epsilon = Query.epsilon,\n  })  : lower = null,\n        includeLower = true,\n        super._();\n\n  /// Where clause that matches all index values equal to the given [value].\n  const IndexWhereClause.equalTo({\n    required this.indexName,\n    required IndexKey value,\n    this.epsilon = Query.epsilon,\n  })  : lower = value,\n        upper = value,\n        includeLower = true,\n        includeUpper = true,\n        super._();\n\n  /// Where clause that matches all index values between the given [lower] and\n  /// [upper] bounds.\n  ///\n  /// For composite indexes, the first elements of the [lower] and [upper] lists\n  /// are checked for equality.\n  const IndexWhereClause.between({\n    required this.indexName,\n    required IndexKey this.lower,\n    this.includeLower = true,\n    required IndexKey this.upper,\n    this.includeUpper = true,\n    this.epsilon = Query.epsilon,\n  }) : super._();\n\n  /// The Isar name of the index to be used.\n  final String indexName;\n\n  /// The lower bound of the where clause.\n  final IndexKey? lower;\n\n  /// Whether the lower bound should be included in the results. Double values\n  /// are never included.\n  final bool includeLower;\n\n  /// The upper bound of the where clause.\n  final IndexKey? upper;\n\n  /// Whether the upper bound should be included in the results. Double values\n  /// are never included.\n  final bool includeUpper;\n\n  /// The precision to use for floating point values.\n  final double epsilon;\n}\n\n/// A where clause traversing objects linked to the specified object.\nclass LinkWhereClause extends WhereClause {\n  /// Create a where clause for the specified link.\n  const LinkWhereClause({\n    required this.linkCollection,\n    required this.linkName,\n    required this.id,\n  }) : super._();\n\n  /// The name of the collection the link originates from.\n  final String linkCollection;\n\n  /// The isar name of the link to be used.\n  final String linkName;\n\n  /// The id of the source object.\n  final Id id;\n}\n\n/// @nodoc\n@protected\nabstract class FilterOperation {\n  const FilterOperation._();\n}\n\n/// The type of dynamic filter conditions.\nenum FilterConditionType {\n  /// Filter checking for equality.\n  equalTo,\n\n  /// Filter matching values greater than the bound.\n  greaterThan,\n\n  /// Filter matching values smaller than the bound.\n  lessThan,\n\n  /// Filter matching values between the bounds.\n  between,\n\n  /// Filter matching String values starting with the prefix.\n  startsWith,\n\n  /// Filter matching String values ending with the suffix.\n  endsWith,\n\n  /// Filter matching String values containing the String.\n  contains,\n\n  /// Filter matching String values matching the wildcard.\n  matches,\n\n  /// Filter matching values that are `null`.\n  isNull,\n\n  /// Filter matching values that are not `null`.\n  isNotNull,\n\n  /// Filter matching lists that contain `null`.\n  elementIsNull,\n\n  /// Filter matching lists that contain an element that is not `null`.\n  elementIsNotNull,\n\n  /// Filter matching the length of a list.\n  listLength,\n}\n\n/// Create a filter condition dynamically.\nclass FilterCondition extends FilterOperation {\n  /// @nodoc\n  @protected\n  const FilterCondition({\n    required this.type,\n    required this.property,\n    this.value1,\n    this.value2,\n    required this.include1,\n    required this.include2,\n    required this.caseSensitive,\n    this.epsilon = Query.epsilon,\n  }) : super._();\n\n  /// Filters the results to only include objects where the property equals\n  /// [value].\n  ///\n  /// For lists, at least one of the values in the list has to match.\n  const FilterCondition.equalTo({\n    required this.property,\n    required Object? value,\n    this.caseSensitive = true,\n    this.epsilon = Query.epsilon,\n  })  : type = FilterConditionType.equalTo,\n        value1 = value,\n        include1 = true,\n        value2 = null,\n        include2 = false,\n        super._();\n\n  /// Filters the results to only include objects where the property is greater\n  /// than [value].\n  ///\n  /// For lists, at least one of the values in the list has to match.\n  const FilterCondition.greaterThan({\n    required this.property,\n    required Object? value,\n    bool include = false,\n    this.caseSensitive = true,\n    this.epsilon = Query.epsilon,\n  })  : type = FilterConditionType.greaterThan,\n        value1 = value,\n        include1 = include,\n        value2 = null,\n        include2 = false,\n        super._();\n\n  /// Filters the results to only include objects where the property is less\n  /// than [value].\n  ///\n  /// For lists, at least one of the values in the list has to match.\n  const FilterCondition.lessThan({\n    required this.property,\n    required Object? value,\n    bool include = false,\n    this.caseSensitive = true,\n    this.epsilon = Query.epsilon,\n  })  : type = FilterConditionType.lessThan,\n        value1 = value,\n        include1 = include,\n        value2 = null,\n        include2 = false,\n        super._();\n\n  /// Filters the results to only include objects where the property is\n  /// between [lower] and [upper].\n  ///\n  /// For lists, at least one of the values in the list has to match.\n  const FilterCondition.between({\n    required this.property,\n    Object? lower,\n    bool includeLower = true,\n    Object? upper,\n    bool includeUpper = true,\n    this.caseSensitive = true,\n    this.epsilon = Query.epsilon,\n  })  : value1 = lower,\n        include1 = includeLower,\n        value2 = upper,\n        include2 = includeUpper,\n        type = FilterConditionType.between,\n        super._();\n\n  /// Filters the results to only include objects where the property starts\n  /// with [value].\n  ///\n  /// For String lists, at least one of the values in the list has to match.\n  const FilterCondition.startsWith({\n    required this.property,\n    required String value,\n    this.caseSensitive = true,\n  })  : type = FilterConditionType.startsWith,\n        value1 = value,\n        include1 = true,\n        value2 = null,\n        include2 = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include objects where the property ends with\n  /// [value].\n  ///\n  /// For String lists, at least one of the values in the list has to match.\n  const FilterCondition.endsWith({\n    required this.property,\n    required String value,\n    this.caseSensitive = true,\n  })  : type = FilterConditionType.endsWith,\n        value1 = value,\n        include1 = true,\n        value2 = null,\n        include2 = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include objects where the String property\n  /// contains [value].\n  ///\n  /// For String lists, at least one of the values in the list has to match.\n  const FilterCondition.contains({\n    required this.property,\n    required String value,\n    this.caseSensitive = true,\n  })  : type = FilterConditionType.contains,\n        value1 = value,\n        include1 = true,\n        value2 = null,\n        include2 = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include objects where the property matches\n  /// the [wildcard].\n  ///\n  /// For String lists, at least one of the values in the list has to match.\n  const FilterCondition.matches({\n    required this.property,\n    required String wildcard,\n    this.caseSensitive = true,\n  })  : type = FilterConditionType.matches,\n        value1 = wildcard,\n        include1 = true,\n        value2 = null,\n        include2 = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include objects where the property is null.\n  const FilterCondition.isNull({\n    required this.property,\n  })  : type = FilterConditionType.isNull,\n        value1 = null,\n        include1 = false,\n        value2 = null,\n        include2 = false,\n        caseSensitive = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include objects where the property is not\n  /// null.\n  const FilterCondition.isNotNull({\n    required this.property,\n  })  : type = FilterConditionType.isNotNull,\n        value1 = null,\n        include1 = false,\n        value2 = null,\n        include2 = false,\n        caseSensitive = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include lists that contain `null`.\n  const FilterCondition.elementIsNull({\n    required this.property,\n  })  : type = FilterConditionType.elementIsNull,\n        value1 = null,\n        include1 = false,\n        value2 = null,\n        include2 = false,\n        caseSensitive = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include lists that do not contain `null`.\n  const FilterCondition.elementIsNotNull({\n    required this.property,\n  })  : type = FilterConditionType.elementIsNotNull,\n        value1 = null,\n        include1 = false,\n        value2 = null,\n        include2 = false,\n        caseSensitive = false,\n        epsilon = Query.epsilon,\n        super._();\n\n  /// Filters the results to only include objects where the length of\n  /// [property] is between [lower] (included) and [upper] (included).\n  ///\n  /// Only list properties are supported.\n  const FilterCondition.listLength({\n    required this.property,\n    required int lower,\n    required int upper,\n  })  : type = FilterConditionType.listLength,\n        value1 = lower,\n        include1 = true,\n        value2 = upper,\n        include2 = true,\n        caseSensitive = false,\n        epsilon = Query.epsilon,\n        assert(lower >= 0 && upper >= 0, 'List length must be positive.'),\n        super._();\n\n  /// Type of the filter condition.\n  final FilterConditionType type;\n\n  /// Property used for comparisons.\n  final String property;\n\n  /// Value used for comparisons. Lower bound for `ConditionType.between`.\n  final Object? value1;\n\n  /// Should `value1` be part of the results.\n  final bool include1;\n\n  /// Upper bound for `ConditionType.between`.\n  final Object? value2;\n\n  /// Should `value1` be part of the results.\n  final bool include2;\n\n  /// Are string operations case sensitive.\n  final bool caseSensitive;\n\n  /// The precision to use for floating point values.\n  final double epsilon;\n}\n\n/// The type of filter groups.\nenum FilterGroupType {\n  /// Logical AND.\n  and,\n\n  /// Logical OR.\n  or,\n\n  /// Logical XOR.\n  xor,\n\n  /// Logical NOT.\n  not,\n}\n\n/// Group one or more filter conditions.\nclass FilterGroup extends FilterOperation {\n  /// @nodoc\n  @protected\n  FilterGroup({\n    required this.type,\n    required this.filters,\n  }) : super._();\n\n  /// Create a logical AND filter group.\n  ///\n  /// Matches when all [filters] match.\n  const FilterGroup.and(this.filters)\n      : type = FilterGroupType.and,\n        super._();\n\n  /// Create a logical OR filter group.\n  ///\n  /// Matches when any of the [filters] matches.\n  const FilterGroup.or(this.filters)\n      : type = FilterGroupType.or,\n        super._();\n\n  /// Create a logical XOR filter group.\n  ///\n  /// Matches when exactly one of the [filters] matches.\n  const FilterGroup.xor(this.filters)\n      : type = FilterGroupType.xor,\n        super._();\n\n  /// Negate a filter.\n  ///\n  /// Matches when any of the [filter] doesn't matches.\n  FilterGroup.not(FilterOperation filter)\n      : filters = [filter],\n        type = FilterGroupType.not,\n        super._();\n\n  /// Type of this group.\n  final FilterGroupType type;\n\n  /// The filter(s) to be grouped.\n  final List<FilterOperation> filters;\n}\n\n/// Sort order\nenum Sort {\n  /// Ascending sort order.\n  asc,\n\n  /// Descending sort order.\n  desc,\n}\n\n/// Property used to sort query results.\nclass SortProperty {\n  /// Create a sort property.\n  const SortProperty({required this.property, required this.sort});\n\n  /// Isar name of the property used for sorting.\n  final String property;\n\n  /// Sort order.\n  final Sort sort;\n}\n\n/// Property used to filter duplicate values.\nclass DistinctProperty {\n  /// Create a distinct property.\n  const DistinctProperty({required this.property, this.caseSensitive});\n\n  /// Isar name of the property used for sorting.\n  final String property;\n\n  /// Should Strings be case sensitive?\n  final bool? caseSensitive;\n}\n\n/// Filter condition based on an embedded object.\nclass ObjectFilter extends FilterOperation {\n  /// Create a filter condition based on an embedded object.\n  const ObjectFilter({\n    required this.property,\n    required this.filter,\n  }) : super._();\n\n  /// Property containing the embedded object(s).\n  final String property;\n\n  /// Filter condition that should be applied\n  final FilterOperation filter;\n}\n\n/// Filter condition based on a link.\nclass LinkFilter extends FilterOperation {\n  /// Create a filter condition based on a link.\n  const LinkFilter({\n    required this.linkName,\n    required FilterOperation this.filter,\n  })  : lower = null,\n        upper = null,\n        super._();\n\n  /// Create a filter condition based on the number of linked objects.\n  const LinkFilter.length({\n    required this.linkName,\n    required int this.lower,\n    required int this.upper,\n  })  : filter = null,\n        assert(lower >= 0 && upper >= 0, 'Link length must be positive.'),\n        super._();\n\n  /// Isar name of the link.\n  final String linkName;\n\n  /// Filter condition that should be applied\n  final FilterOperation? filter;\n\n  /// The minumum number of linked objects\n  final int? lower;\n\n  /// The maximum number of linked objects\n  final int? upper;\n}\n"
  },
  {
    "path": "packages/isar/lib/src/schema/collection_schema.dart",
    "content": "part of isar;\n\n/// This schema represents a collection.\nclass CollectionSchema<OBJ> extends Schema<OBJ> {\n  /// @nodoc\n  @protected\n  const CollectionSchema({\n    required super.id,\n    required super.name,\n    required super.properties,\n    required super.estimateSize,\n    required super.serialize,\n    required super.deserialize,\n    required super.deserializeProp,\n    required this.idName,\n    required this.indexes,\n    required this.links,\n    required this.embeddedSchemas,\n    required this.getId,\n    required this.getLinks,\n    required this.attach,\n    required this.version,\n  }) : assert(\n          Isar.version == version,\n          'Outdated generated code. Please re-run code '\n          'generation using the latest generator.',\n        );\n\n  /// @nodoc\n  @protected\n  factory CollectionSchema.fromJson(Map<String, dynamic> json) {\n    final collection = Schema<dynamic>.fromJson(json);\n    return CollectionSchema(\n      id: collection.id,\n      name: collection.name,\n      properties: collection.properties,\n      idName: json['idName'] as String,\n      indexes: {\n        for (final index in json['indexes'] as List<dynamic>)\n          (index as Map<String, dynamic>)['name'] as String:\n              IndexSchema.fromJson(index),\n      },\n      links: {\n        for (final link in json['links'] as List<dynamic>)\n          (link as Map<String, dynamic>)['name'] as String:\n              LinkSchema.fromJson(link),\n      },\n      embeddedSchemas: {\n        for (final schema in json['embeddedSchemas'] as List<dynamic>)\n          (schema as Map<String, dynamic>)['name'] as String:\n              Schema.fromJson(schema),\n      },\n      estimateSize: (_, __, ___) => throw UnimplementedError(),\n      serialize: (_, __, ___, ____) => throw UnimplementedError(),\n      deserialize: (_, __, ___, ____) => throw UnimplementedError(),\n      deserializeProp: (_, __, ___, ____) => throw UnimplementedError(),\n      getId: (_) => throw UnimplementedError(),\n      getLinks: (_) => throw UnimplementedError(),\n      attach: (_, __, ___) => throw UnimplementedError(),\n      version: Isar.version,\n    );\n  }\n\n  /// Name of the id property\n  final String idName;\n\n  @override\n  bool get embedded => false;\n\n  /// A map of name -> index pairs\n  final Map<String, IndexSchema> indexes;\n\n  /// A map of name -> link pairs\n  final Map<String, LinkSchema> links;\n\n  /// A map of name -> embedded schema pairs\n  final Map<String, Schema<dynamic>> embeddedSchemas;\n\n  /// @nodoc\n  final GetId<OBJ> getId;\n\n  /// @nodoc\n  final GetLinks<OBJ> getLinks;\n\n  /// @nodoc\n  final Attach<OBJ> attach;\n\n  /// @nodoc\n  final String version;\n\n  /// @nodoc\n  void toCollection(void Function<OBJ>() callback) => callback<OBJ>();\n\n  /// @nodoc\n  @pragma('vm:prefer-inline')\n  IndexSchema index(String indexName) {\n    final index = indexes[indexName];\n    if (index != null) {\n      return index;\n    } else {\n      throw IsarError('Unknown index \"$indexName\"');\n    }\n  }\n\n  /// @nodoc\n  @pragma('vm:prefer-inline')\n  LinkSchema link(String linkName) {\n    final link = links[linkName];\n    if (link != null) {\n      return link;\n    } else {\n      throw IsarError('Unknown link \"$linkName\"');\n    }\n  }\n\n  /// @nodoc\n  @protected\n  @override\n  Map<String, dynamic> toJson() {\n    final json = {\n      ...super.toJson(),\n      'idName': idName,\n      'indexes': [\n        for (final index in indexes.values) index.toJson(),\n      ],\n      'links': [\n        for (final link in links.values) link.toJson(),\n      ],\n    };\n\n    assert(() {\n      json['embeddedSchemas'] = [\n        for (final schema in embeddedSchemas.values) schema.toJson(),\n      ];\n      return true;\n    }());\n\n    return json;\n  }\n}\n\n/// @nodoc\n@protected\ntypedef GetId<T> = Id Function(T object);\n\n/// @nodoc\n@protected\ntypedef GetLinks<T> = List<IsarLinkBase<dynamic>> Function(T object);\n\n/// @nodoc\n@protected\ntypedef Attach<T> = void Function(IsarCollection<T> col, Id id, T object);\n"
  },
  {
    "path": "packages/isar/lib/src/schema/index_schema.dart",
    "content": "part of isar;\n\n/// This schema represents an index.\nclass IndexSchema {\n  /// @nodoc\n  @protected\n  const IndexSchema({\n    required this.id,\n    required this.name,\n    required this.unique,\n    required this.replace,\n    required this.properties,\n  });\n\n  /// @nodoc\n  @protected\n  factory IndexSchema.fromJson(Map<String, dynamic> json) {\n    return IndexSchema(\n      id: -1,\n      name: json['name'] as String,\n      unique: json['unique'] as bool,\n      replace: json['replace'] as bool,\n      properties: (json['properties'] as List<dynamic>)\n          .map((e) => IndexPropertySchema.fromJson(e as Map<String, dynamic>))\n          .toList(),\n    );\n  }\n\n  /// Internal id of this index.\n  final int id;\n\n  /// Name of this index.\n  final String name;\n\n  /// Whether duplicates are disallowed in this index.\n  final bool unique;\n\n  /// Whether duplocates will be replaced or throw an error.\n  final bool replace;\n\n  /// Composite properties.\n  final List<IndexPropertySchema> properties;\n\n  /// @nodoc\n  @protected\n  Map<String, dynamic> toJson() {\n    final json = {\n      'name': name,\n      'unique': unique,\n      'replace': replace,\n      'properties': [\n        for (final property in properties) property.toJson(),\n      ],\n    };\n\n    return json;\n  }\n}\n\n/// This schema represents a composite index property.\nclass IndexPropertySchema {\n  /// @nodoc\n  @protected\n  const IndexPropertySchema({\n    required this.name,\n    required this.type,\n    required this.caseSensitive,\n  });\n\n  /// @nodoc\n  @protected\n  factory IndexPropertySchema.fromJson(Map<String, dynamic> json) {\n    return IndexPropertySchema(\n      name: json['name'] as String,\n      type: IndexType.values.firstWhere((e) => _typeName[e] == json['type']),\n      caseSensitive: json['caseSensitive'] as bool,\n    );\n  }\n\n  /// Isar name of the property.\n  final String name;\n\n  /// Type of index.\n  final IndexType type;\n\n  /// Whether String properties should be stored with casing.\n  final bool caseSensitive;\n\n  /// @nodoc\n  @protected\n  Map<String, dynamic> toJson() {\n    return {\n      'name': name,\n      'type': _typeName[type],\n      'caseSensitive': caseSensitive,\n    };\n  }\n\n  static const _typeName = {\n    IndexType.value: 'Value',\n    IndexType.hash: 'Hash',\n    IndexType.hashElements: 'HashElements',\n  };\n}\n"
  },
  {
    "path": "packages/isar/lib/src/schema/link_schema.dart",
    "content": "part of isar;\n\n/// This schema represents a link to the same or another collection.\nclass LinkSchema {\n  /// @nodoc\n  @protected\n  const LinkSchema({\n    required this.id,\n    required this.name,\n    required this.target,\n    required this.single,\n    this.linkName,\n  });\n\n  /// @nodoc\n  @protected\n  factory LinkSchema.fromJson(Map<String, dynamic> json) {\n    return LinkSchema(\n      id: -1,\n      name: json['name'] as String,\n      target: json['target'] as String,\n      single: json['single'] as bool,\n      linkName: json['linkName'] as String?,\n    );\n  }\n\n  /// Internal id of this link.\n  final int id;\n\n  /// Name of this link.\n  final String name;\n\n  /// Isar name of the target collection.\n  final String target;\n\n  /// Whether this is link can only hold a single target object.\n  final bool single;\n\n  /// If this is a backlink, [linkName] is the name of the source link in the\n  /// [target] collection.\n  final String? linkName;\n\n  /// Whether this link is a backlink.\n  bool get isBacklink => linkName != null;\n\n  /// @nodoc\n  @protected\n  Map<String, dynamic> toJson() {\n    final json = <String, dynamic>{\n      'name': name,\n      'target': target,\n      'single': single,\n    };\n\n    assert(() {\n      if (linkName != null) {\n        json['linkName'] = linkName;\n      }\n      return true;\n    }());\n\n    return json;\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/schema/property_schema.dart",
    "content": "part of isar;\n\n/// A single propery of a collection or embedded object.\nclass PropertySchema {\n  /// @nodoc\n  @protected\n  const PropertySchema({\n    required this.id,\n    required this.name,\n    required this.type,\n    this.enumMap,\n    this.target,\n  });\n\n  /// @nodoc\n  @protected\n  factory PropertySchema.fromJson(Map<String, dynamic> json) {\n    return PropertySchema(\n      id: -1,\n      name: json['name'] as String,\n      type: IsarType.values.firstWhere((e) => e.schemaName == json['type']),\n      enumMap: json['enumMap'] as Map<String, dynamic>?,\n      target: json['target'] as String?,\n    );\n  }\n\n  /// Internal id of this property.\n  final int id;\n\n  /// Name of the property\n  final String name;\n\n  /// Isar type of the property\n  final IsarType type;\n\n  /// Maps enum names to database values\n  final Map<String, dynamic>? enumMap;\n\n  /// For embedded objects: Name of the target schema\n  final String? target;\n\n  /// @nodoc\n  @protected\n  Map<String, dynamic> toJson() {\n    final json = <String, dynamic>{\n      'name': name,\n      'type': type.schemaName,\n      if (target != null) 'target': target,\n    };\n\n    assert(() {\n      if (enumMap != null) {\n        json['enumMap'] = enumMap;\n      }\n      return true;\n    }());\n\n    return json;\n  }\n}\n\n/// Supported Isar types\nenum IsarType {\n  /// Boolean\n  bool('Bool'),\n\n  /// 8-bit unsigned integer\n  byte('Byte'),\n\n  /// 32-bit singed integer\n  int('Int'),\n\n  /// 32-bit float\n  float('Float'),\n\n  /// 64-bit singed integer\n  long('Long'),\n\n  /// 64-bit float\n  double('Double'),\n\n  /// DateTime\n  dateTime('DateTime'),\n\n  /// String\n  string('String'),\n\n  /// Embedded object\n  object('Object'),\n\n  /// Boolean list\n  boolList('BoolList'),\n\n  /// 8-bit unsigned integer list\n  byteList('ByteList'),\n\n  /// 32-bit singed integer list\n  intList('IntList'),\n\n  /// 32-bit float list\n  floatList('FloatList'),\n\n  /// 64-bit singed integer list\n  longList('LongList'),\n\n  /// 64-bit float list\n  doubleList('DoubleList'),\n\n  /// DateTime list\n  dateTimeList('DateTimeList'),\n\n  /// String list\n  stringList('StringList'),\n\n  /// Embedded object list\n  objectList('ObjectList');\n\n  /// @nodoc\n  const IsarType(this.schemaName);\n\n  /// @nodoc\n  final String schemaName;\n}\n\n/// @nodoc\nextension IsarTypeX on IsarType {\n  /// Whether this type represents a list\n  bool get isList => index >= IsarType.boolList.index;\n\n  /// @nodoc\n  IsarType get scalarType {\n    switch (this) {\n      case IsarType.boolList:\n        return IsarType.bool;\n      case IsarType.byteList:\n        return IsarType.byte;\n      case IsarType.intList:\n        return IsarType.int;\n      case IsarType.floatList:\n        return IsarType.float;\n      case IsarType.longList:\n        return IsarType.long;\n      case IsarType.doubleList:\n        return IsarType.double;\n      case IsarType.dateTimeList:\n        return IsarType.dateTime;\n      case IsarType.stringList:\n        return IsarType.string;\n      case IsarType.objectList:\n        return IsarType.object;\n      // ignore: no_default_cases\n      default:\n        return this;\n    }\n  }\n\n  /// @nodoc\n  IsarType get listType {\n    switch (this) {\n      case IsarType.bool:\n        return IsarType.boolList;\n      case IsarType.byte:\n        return IsarType.byteList;\n      case IsarType.int:\n        return IsarType.intList;\n      case IsarType.float:\n        return IsarType.floatList;\n      case IsarType.long:\n        return IsarType.longList;\n      case IsarType.double:\n        return IsarType.doubleList;\n      case IsarType.dateTime:\n        return IsarType.dateTimeList;\n      case IsarType.string:\n        return IsarType.stringList;\n      case IsarType.object:\n        return IsarType.objectList;\n      // ignore: no_default_cases\n      default:\n        return this;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/schema/schema.dart",
    "content": "part of isar;\n\n/// This schema either represents a collection or embedded object.\nclass Schema<OBJ> {\n  /// @nodoc\n  @protected\n  const Schema({\n    required this.id,\n    required this.name,\n    required this.properties,\n    required this.estimateSize,\n    required this.serialize,\n    required this.deserialize,\n    required this.deserializeProp,\n  });\n\n  /// @nodoc\n  @protected\n  factory Schema.fromJson(Map<String, dynamic> json) {\n    return Schema(\n      id: -1,\n      name: json['name'] as String,\n      properties: {\n        for (final property in json['properties'] as List<dynamic>)\n          (property as Map<String, dynamic>)['name'] as String:\n              PropertySchema.fromJson(property),\n      },\n      estimateSize: (_, __, ___) => throw UnimplementedError(),\n      serialize: (_, __, ___, ____) => throw UnimplementedError(),\n      deserialize: (_, __, ___, ____) => throw UnimplementedError(),\n      deserializeProp: (_, __, ___, ____) => throw UnimplementedError(),\n    );\n  }\n\n  /// Internal id of this collection or embedded object.\n  final int id;\n\n  /// Name of the collection or embedded object\n  final String name;\n\n  /// Whether this is an embedded object\n  bool get embedded => true;\n\n  /// A map of name -> property pairs\n  final Map<String, PropertySchema> properties;\n\n  /// @nodoc\n  @protected\n  final EstimateSize<OBJ> estimateSize;\n\n  /// @nodoc\n  @protected\n  final Serialize<OBJ> serialize;\n\n  /// @nodoc\n  @protected\n  final Deserialize<OBJ> deserialize;\n\n  /// @nodoc\n  @protected\n  final DeserializeProp deserializeProp;\n\n  /// Returns a property by its name or throws an error.\n  @pragma('vm:prefer-inline')\n  PropertySchema property(String propertyName) {\n    final property = properties[propertyName];\n    if (property != null) {\n      return property;\n    } else {\n      throw IsarError('Unknown property \"$propertyName\"');\n    }\n  }\n\n  /// @nodoc\n  @protected\n  Map<String, dynamic> toJson() {\n    final json = {\n      'name': name,\n      'embedded': embedded,\n      'properties': [\n        for (final property in properties.values) property.toJson(),\n      ],\n    };\n\n    return json;\n  }\n\n  /// @nodoc\n  @protected\n  Type get type => OBJ;\n}\n\n/// @nodoc\n@protected\ntypedef EstimateSize<T> = int Function(\n  T object,\n  List<int> offsets,\n  Map<Type, List<int>> allOffsets,\n);\n\n/// @nodoc\n@protected\ntypedef Serialize<T> = void Function(\n  T object,\n  IsarWriter writer,\n  List<int> offsets,\n  Map<Type, List<int>> allOffsets,\n);\n\n/// @nodoc\n@protected\ntypedef Deserialize<T> = T Function(\n  Id id,\n  IsarReader reader,\n  List<int> offsets,\n  Map<Type, List<int>> allOffsets,\n);\n\n/// @nodoc\n@protected\ntypedef DeserializeProp = dynamic Function(\n  IsarReader reader,\n  int propertyId,\n  int offset,\n  Map<Type, List<int>> allOffsets,\n);\n"
  },
  {
    "path": "packages/isar/lib/src/web/bindings.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:indexed_db';\nimport 'dart:js';\n\nimport 'package:isar/isar.dart';\nimport 'package:js/js.dart';\nimport 'package:js/js_util.dart';\n\n@JS('JSON.stringify')\nexternal String stringify(dynamic value);\n\n@JS('indexedDB.cmp')\nexternal int idbCmp(dynamic value1, dynamic value2);\n\n@JS('Object.keys')\nexternal List<String> objectKeys(dynamic obj);\n\nMap<String, dynamic> jsMapToDart(Object obj) {\n  final keys = objectKeys(obj);\n  final map = <String, dynamic>{};\n  for (final key in keys) {\n    map[key] = getProperty<dynamic>(obj, key);\n  }\n  return map;\n}\n\n@JS('Promise')\nclass Promise {}\n\nextension PromiseX on Promise {\n  Future<T> wait<T>() => promiseToFuture(this);\n}\n\n@JS('openIsar')\nexternal Promise openIsarJs(\n  String name,\n  List<dynamic> schemas,\n  bool relaxedDurability,\n);\n\n@JS('IsarTxn')\nclass IsarTxnJs {\n  external Promise commit();\n\n  external void abort();\n\n  external bool get write;\n}\n\n@JS('IsarInstance')\nclass IsarInstanceJs {\n  external IsarTxnJs beginTxn(bool write);\n\n  external IsarCollectionJs getCollection(String name);\n\n  external Promise close(bool deleteFromDisk);\n}\n\ntypedef ChangeCallbackJs = void Function();\n\ntypedef ObjectChangeCallbackJs = void Function(Object? object);\n\ntypedef QueryChangeCallbackJs = void Function(List<dynamic> results);\n\ntypedef StopWatchingJs = JsFunction;\n\n@JS('IsarCollection')\nclass IsarCollectionJs {\n  external IsarLinkJs getLink(String name);\n\n  external Promise getAll(IsarTxnJs txn, List<Id> ids);\n\n  external Promise getAllByIndex(\n    IsarTxnJs txn,\n    String indexName,\n    List<List<dynamic>> values,\n  );\n\n  external Promise putAll(IsarTxnJs txn, List<dynamic> objects);\n\n  external Promise deleteAll(IsarTxnJs txn, List<Id> ids);\n\n  external Promise deleteAllByIndex(\n    IsarTxnJs txn,\n    String indexName,\n    List<dynamic> keys,\n  );\n\n  external Promise clear(IsarTxnJs txn);\n\n  external StopWatchingJs watchLazy(ChangeCallbackJs callback);\n\n  external StopWatchingJs watchObject(Id id, ObjectChangeCallbackJs callback);\n\n  external StopWatchingJs watchQuery(\n    QueryJs query,\n    QueryChangeCallbackJs callback,\n  );\n\n  external StopWatchingJs watchQueryLazy(\n    QueryJs query,\n    ChangeCallbackJs callback,\n  );\n}\n\n@JS('IsarLink')\nclass IsarLinkJs {\n  external Promise update(\n    IsarTxnJs txn,\n    bool backlink,\n    Id id,\n    List<Id> addedTargets,\n    List<Id> deletedTargets,\n  );\n\n  external Promise clear(IsarTxnJs txn, Id id, bool backlink);\n}\n\n@JS('IdWhereClause')\n@anonymous\nclass IdWhereClauseJs {\n  external KeyRange? range;\n}\n\n@JS('IndexWhereClause')\n@anonymous\nclass IndexWhereClauseJs {\n  external String indexName;\n  external KeyRange? range;\n}\n\n@JS('LinkWhereClause')\n@anonymous\nclass LinkWhereClauseJs {\n  external String linkCollection;\n  external String linkName;\n  external bool backlink;\n  external Id id;\n}\n\n@JS('Function')\nclass FilterJs {\n  external FilterJs(String id, String obj, String method);\n}\n\n@JS('Function')\nclass SortCmpJs {\n  external SortCmpJs(String a, String b, String method);\n}\n\n@JS('Function')\nclass DistinctValueJs {\n  external DistinctValueJs(String obj, String method);\n}\n\n@JS('IsarQuery')\nclass QueryJs {\n  external QueryJs(\n    IsarCollectionJs collection,\n    List<dynamic> whereClauses,\n    bool whereDistinct,\n    bool whereAscending,\n    FilterJs? filter,\n    SortCmpJs? sortCmp,\n    DistinctValueJs? distinctValue,\n    int? offset,\n    int? limit,\n  );\n\n  external Promise findFirst(IsarTxnJs txn);\n\n  external Promise findAll(IsarTxnJs txn);\n\n  external Promise deleteFirst(IsarTxnJs txn);\n\n  external Promise deleteAll(IsarTxnJs txn);\n\n  external Promise min(IsarTxnJs txn, String propertyName);\n\n  external Promise max(IsarTxnJs txn, String propertyName);\n\n  external Promise sum(IsarTxnJs txn, String propertyName);\n\n  external Promise average(IsarTxnJs txn, String propertyName);\n\n  external Promise count(IsarTxnJs txn);\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/isar_collection_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs, invalid_use_of_protected_member\n\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:js';\nimport 'dart:js_util';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/web/bindings.dart';\nimport 'package:isar/src/web/isar_impl.dart';\nimport 'package:isar/src/web/isar_reader_impl.dart';\nimport 'package:isar/src/web/isar_web.dart';\nimport 'package:isar/src/web/isar_writer_impl.dart';\nimport 'package:isar/src/web/query_build.dart';\nimport 'package:meta/dart2js.dart';\n\nclass IsarCollectionImpl<OBJ> extends IsarCollection<OBJ> {\n  IsarCollectionImpl({\n    required this.isar,\n    required this.native,\n    required this.schema,\n  });\n\n  @override\n  final IsarImpl isar;\n  final IsarCollectionJs native;\n\n  @override\n  final CollectionSchema<OBJ> schema;\n\n  @override\n  String get name => schema.name;\n\n  late final _offsets = isar.offsets[OBJ]!;\n\n  @tryInline\n  OBJ deserializeObject(Object object) {\n    final id = getProperty<int>(object, idName);\n    final reader = IsarReaderImpl(object);\n    return schema.deserialize(id, reader, _offsets, isar.offsets);\n  }\n\n  @tryInline\n  List<OBJ?> deserializeObjects(dynamic objects) {\n    final list = objects as List;\n    final results = <OBJ?>[];\n    for (final object in list) {\n      results.add(object is Object ? deserializeObject(object) : null);\n    }\n    return results;\n  }\n\n  @override\n  Future<List<OBJ?>> getAll(List<Id> ids) {\n    return isar.getTxn(false, (IsarTxnJs txn) async {\n      final objects = await native.getAll(txn, ids).wait<List<Object?>>();\n      return deserializeObjects(objects);\n    });\n  }\n\n  @override\n  Future<List<OBJ?>> getAllByIndex(String indexName, List<IndexKey> keys) {\n    return isar.getTxn(false, (IsarTxnJs txn) async {\n      final objects = await native\n          .getAllByIndex(txn, indexName, keys)\n          .wait<List<Object?>>();\n      return deserializeObjects(objects);\n    });\n  }\n\n  @override\n  List<OBJ?> getAllSync(List<Id> ids) => unsupportedOnWeb();\n\n  @override\n  List<OBJ?> getAllByIndexSync(String indexName, List<IndexKey> keys) =>\n      unsupportedOnWeb();\n\n  @override\n  Future<List<Id>> putAll(List<OBJ> objects) {\n    return putAllByIndex(null, objects);\n  }\n\n  @override\n  List<int> putAllSync(List<OBJ> objects, {bool saveLinks = true}) =>\n      unsupportedOnWeb();\n\n  @override\n  Future<List<Id>> putAllByIndex(String? indexName, List<OBJ> objects) {\n    return isar.getTxn(true, (IsarTxnJs txn) async {\n      final serialized = <Object>[];\n      for (final object in objects) {\n        final jsObj = newObject<Object>();\n        final writer = IsarWriterImpl(jsObj);\n        schema.serialize(object, writer, _offsets, isar.offsets);\n        setProperty(jsObj, idName, schema.getId(object));\n        serialized.add(jsObj);\n      }\n      final ids = await native.putAll(txn, serialized).wait<List<dynamic>>();\n      for (var i = 0; i < objects.length; i++) {\n        final object = objects[i];\n        final id = ids[i] as Id;\n        schema.attach(this, id, object);\n      }\n\n      return ids.cast<Id>().toList();\n    });\n  }\n\n  @override\n  List<Id> putAllByIndexSync(\n    String indexName,\n    List<OBJ> objects, {\n    bool saveLinks = true,\n  }) =>\n      unsupportedOnWeb();\n\n  @override\n  Future<int> deleteAll(List<Id> ids) async {\n    await isar.getTxn(true, (IsarTxnJs txn) {\n      return native.deleteAll(txn, ids).wait<void>();\n    });\n    return ids.length;\n  }\n\n  @override\n  Future<int> deleteAllByIndex(String indexName, List<IndexKey> keys) {\n    return isar.getTxn(true, (IsarTxnJs txn) {\n      return native.deleteAllByIndex(txn, indexName, keys).wait();\n    });\n  }\n\n  @override\n  int deleteAllSync(List<Id> ids) => unsupportedOnWeb();\n\n  @override\n  int deleteAllByIndexSync(String indexName, List<IndexKey> keys) =>\n      unsupportedOnWeb();\n\n  @override\n  Future<void> clear() {\n    return isar.getTxn(true, (IsarTxnJs txn) {\n      return native.clear(txn).wait();\n    });\n  }\n\n  @override\n  void clearSync() => unsupportedOnWeb();\n\n  @override\n  Future<void> importJson(List<Map<String, dynamic>> json) {\n    return isar.getTxn(true, (IsarTxnJs txn) async {\n      await native.putAll(txn, json.map(jsify).toList()).wait<dynamic>();\n    });\n  }\n\n  @override\n  Future<void> importJsonRaw(Uint8List jsonBytes) {\n    final json = jsonDecode(const Utf8Decoder().convert(jsonBytes)) as List;\n    return importJson(json.cast());\n  }\n\n  @override\n  void importJsonSync(List<Map<String, dynamic>> json) => unsupportedOnWeb();\n\n  @override\n  void importJsonRawSync(Uint8List jsonBytes) => unsupportedOnWeb();\n\n  @override\n  Future<int> count() => where().count();\n\n  @override\n  int countSync() => unsupportedOnWeb();\n\n  @override\n  Future<int> getSize({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) =>\n      unsupportedOnWeb();\n\n  @override\n  int getSizeSync({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) =>\n      unsupportedOnWeb();\n\n  @override\n  Stream<void> watchLazy({bool fireImmediately = false}) {\n    JsFunction? stop;\n    final controller = StreamController<void>(\n      onCancel: () {\n        stop?.apply([]);\n      },\n    );\n\n    final void Function() callback = allowInterop(() => controller.add(null));\n    stop = native.watchLazy(callback);\n\n    return controller.stream;\n  }\n\n  @override\n  Stream<OBJ?> watchObject(\n    Id id, {\n    bool fireImmediately = false,\n    bool deserialize = true,\n  }) {\n    JsFunction? stop;\n    final controller = StreamController<OBJ?>(\n      onCancel: () {\n        stop?.apply([]);\n      },\n    );\n\n    final Null Function(Object? obj) callback = allowInterop((Object? obj) {\n      final object = deserialize && obj != null ? deserializeObject(obj) : null;\n      controller.add(object);\n    });\n    stop = native.watchObject(id, callback);\n\n    return controller.stream;\n  }\n\n  @override\n  Stream<void> watchObjectLazy(Id id, {bool fireImmediately = false}) =>\n      watchObject(id, deserialize: false);\n\n  @override\n  Query<T> buildQuery<T>({\n    List<WhereClause> whereClauses = const [],\n    bool whereDistinct = false,\n    Sort whereSort = Sort.asc,\n    FilterOperation? filter,\n    List<SortProperty> sortBy = const [],\n    List<DistinctProperty> distinctBy = const [],\n    int? offset,\n    int? limit,\n    String? property,\n  }) {\n    return buildWebQuery(\n      this,\n      whereClauses,\n      whereDistinct,\n      whereSort,\n      filter,\n      sortBy,\n      distinctBy,\n      offset,\n      limit,\n      property,\n    );\n  }\n\n  @override\n  Future<void> verify(List<OBJ> objects) => unsupportedOnWeb();\n\n  @override\n  Future<void> verifyLink(\n    String linkName,\n    List<int> sourceIds,\n    List<int> targetIds,\n  ) =>\n      unsupportedOnWeb();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/isar_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:async';\nimport 'dart:html';\n\nimport 'package:isar/isar.dart';\n\nimport 'package:isar/src/web/bindings.dart';\nimport 'package:isar/src/web/isar_web.dart';\n\nconst Symbol _zoneTxn = #zoneTxn;\n\nclass IsarImpl extends Isar {\n  IsarImpl(super.name, this.instance);\n\n  final IsarInstanceJs instance;\n  final offsets = <Type, List<int>>{};\n  final List<Future<void>> _activeAsyncTxns = [];\n\n  @override\n  final String? directory = null;\n\n  void requireNotInTxn() {\n    if (Zone.current[_zoneTxn] != null) {\n      throw IsarError(\n        'Cannot perform this operation from within an active transaction.',\n      );\n    }\n  }\n\n  Future<T> _txn<T>(\n    bool write,\n    bool silent,\n    Future<T> Function() callback,\n  ) async {\n    requireOpen();\n    requireNotInTxn();\n\n    final completer = Completer<void>();\n    _activeAsyncTxns.add(completer.future);\n\n    final txn = instance.beginTxn(write);\n\n    final zone = Zone.current.fork(\n      zoneValues: {_zoneTxn: txn},\n    );\n\n    T result;\n    try {\n      result = await zone.run(callback);\n      await txn.commit().wait<dynamic>();\n    } catch (e) {\n      txn.abort();\n      if (e is DomException) {\n        if (e.name == DomException.CONSTRAINT) {\n          throw IsarUniqueViolationError();\n        } else {\n          throw IsarError('${e.name}: ${e.message}');\n        }\n      } else {\n        rethrow;\n      }\n    } finally {\n      completer.complete();\n      _activeAsyncTxns.remove(completer.future);\n    }\n\n    return result;\n  }\n\n  @override\n  Future<T> txn<T>(Future<T> Function() callback) {\n    return _txn(false, false, callback);\n  }\n\n  @override\n  Future<T> writeTxn<T>(Future<T> Function() callback, {bool silent = false}) {\n    return _txn(true, silent, callback);\n  }\n\n  @override\n  T txnSync<T>(T Function() callback) => unsupportedOnWeb();\n\n  @override\n  T writeTxnSync<T>(T Function() callback, {bool silent = false}) =>\n      unsupportedOnWeb();\n\n  Future<T> getTxn<T>(bool write, Future<T> Function(IsarTxnJs txn) callback) {\n    final currentTxn = Zone.current[_zoneTxn] as IsarTxnJs?;\n    if (currentTxn != null) {\n      if (write && !currentTxn.write) {\n        throw IsarError(\n          'Operation cannot be performed within a read transaction.',\n        );\n      }\n      return callback(currentTxn);\n    } else if (!write) {\n      return _txn(false, false, () {\n        return callback(Zone.current[_zoneTxn] as IsarTxnJs);\n      });\n    } else {\n      throw IsarError('Write operations require an explicit transaction.');\n    }\n  }\n\n  @override\n  Future<int> getSize({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) =>\n      unsupportedOnWeb();\n\n  @override\n  int getSizeSync({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) =>\n      unsupportedOnWeb();\n\n  @override\n  Future<void> copyToFile(String targetPath) => unsupportedOnWeb();\n\n  @override\n  Future<bool> close({bool deleteFromDisk = false}) async {\n    requireOpen();\n    requireNotInTxn();\n    await Future.wait(_activeAsyncTxns);\n    await super.close();\n    await instance.close(deleteFromDisk).wait<dynamic>();\n    return true;\n  }\n\n  @override\n  Future<void> verify() => unsupportedOnWeb();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/isar_link_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/common/isar_link_base_impl.dart';\nimport 'package:isar/src/common/isar_link_common.dart';\nimport 'package:isar/src/common/isar_links_common.dart';\nimport 'package:isar/src/web/bindings.dart';\nimport 'package:isar/src/web/isar_collection_impl.dart';\nimport 'package:isar/src/web/isar_web.dart';\n\nmixin IsarLinkBaseMixin<OBJ> on IsarLinkBaseImpl<OBJ> {\n  @override\n  IsarCollectionImpl<dynamic> get sourceCollection =>\n      super.sourceCollection as IsarCollectionImpl;\n\n  @override\n  IsarCollectionImpl<OBJ> get targetCollection =>\n      super.targetCollection as IsarCollectionImpl<OBJ>;\n\n  @override\n  late final Id Function(OBJ) getId = targetCollection.schema.getId;\n\n  late final String? backlinkLinkName =\n      sourceCollection.schema.link(linkName).linkName;\n\n  late final IsarLinkJs jsLink = backlinkLinkName != null\n      ? targetCollection.native.getLink(backlinkLinkName!)\n      : sourceCollection.native.getLink(linkName);\n\n  @override\n  Future<void> update({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  }) {\n    final linkList = link.toList();\n    final unlinkList = unlink.toList();\n\n    final containingId = requireAttached();\n    final backlink = backlinkLinkName != null;\n\n    final linkIds = List<Id>.filled(linkList.length, 0);\n    for (var i = 0; i < linkList.length; i++) {\n      linkIds[i] = requireGetId(linkList[i]);\n    }\n\n    final unlinkIds = List<Id>.filled(unlinkList.length, 0);\n    for (var i = 0; i < unlinkList.length; i++) {\n      unlinkIds[i] = requireGetId(unlinkList[i]);\n    }\n\n    return targetCollection.isar.getTxn(true, (IsarTxnJs txn) async {\n      if (reset) {\n        await jsLink.clear(txn, containingId, backlink).wait<dynamic>();\n      }\n      return jsLink\n          .update(txn, backlink, containingId, linkIds, unlinkIds)\n          .wait();\n    });\n  }\n\n  @override\n  void updateSync({\n    Iterable<OBJ> link = const [],\n    Iterable<OBJ> unlink = const [],\n    bool reset = false,\n  }) =>\n      unsupportedOnWeb();\n}\n\nclass IsarLinkImpl<OBJ> extends IsarLinkCommon<OBJ>\n    with IsarLinkBaseMixin<OBJ> {}\n\nclass IsarLinksImpl<OBJ> extends IsarLinksCommon<OBJ>\n    with IsarLinkBaseMixin<OBJ> {}\n"
  },
  {
    "path": "packages/isar/lib/src/web/isar_reader_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'package:isar/isar.dart';\nimport 'package:js/js_util.dart';\nimport 'package:meta/dart2js.dart';\n\nconst nullNumber = double.negativeInfinity;\nconst idName = '_id';\nfinal nullDate = DateTime.fromMillisecondsSinceEpoch(0);\n\nclass IsarReaderImpl implements IsarReader {\n  IsarReaderImpl(this.object);\n\n  final Object object;\n\n  @tryInline\n  @override\n  bool readBool(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value == 1;\n  }\n\n  @tryInline\n  @override\n  bool? readBoolOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value == 0\n        ? false\n        : value == 1\n            ? true\n            : null;\n  }\n\n  @tryInline\n  @override\n  int readByte(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int ? value : nullNumber as int;\n  }\n\n  @tryInline\n  @override\n  int? readByteOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int && value != nullNumber ? value : null;\n  }\n\n  @tryInline\n  @override\n  int readInt(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int ? value : nullNumber as int;\n  }\n\n  @tryInline\n  @override\n  int? readIntOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int && value != nullNumber ? value : null;\n  }\n\n  @tryInline\n  @override\n  double readFloat(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is double ? value : nullNumber;\n  }\n\n  @tryInline\n  @override\n  double? readFloatOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is double && value != nullNumber ? value : null;\n  }\n\n  @tryInline\n  @override\n  int readLong(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int ? value : nullNumber as int;\n  }\n\n  @tryInline\n  @override\n  int? readLongOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int && value != nullNumber ? value : null;\n  }\n\n  @tryInline\n  @override\n  double readDouble(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is double && value != nullNumber ? value : nullNumber;\n  }\n\n  @tryInline\n  @override\n  double? readDoubleOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is double && value != nullNumber ? value : null;\n  }\n\n  @tryInline\n  @override\n  DateTime readDateTime(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int && value != nullNumber\n        ? DateTime.fromMillisecondsSinceEpoch(value, isUtc: true).toLocal()\n        : nullDate;\n  }\n\n  @tryInline\n  @override\n  DateTime? readDateTimeOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is int && value != nullNumber\n        ? DateTime.fromMillisecondsSinceEpoch(value, isUtc: true).toLocal()\n        : null;\n  }\n\n  @tryInline\n  @override\n  String readString(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is String ? value : '';\n  }\n\n  @tryInline\n  @override\n  String? readStringOrNull(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is String ? value : null;\n  }\n\n  @tryInline\n  @override\n  T? readObjectOrNull<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n  ) {\n    final value = getProperty<dynamic>(object, offset);\n    if (value is Object) {\n      final reader = IsarReaderImpl(value);\n      return deserialize(0, reader, allOffsets[T]!, allOffsets);\n    } else {\n      return null;\n    }\n  }\n\n  @tryInline\n  @override\n  List<bool>? readBoolList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List ? value.map((e) => e == 1).toList() : null;\n  }\n\n  @tryInline\n  @override\n  List<bool?>? readBoolOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value\n            .map(\n              (e) => e == 0\n                  ? false\n                  : e == 1\n                      ? true\n                      : null,\n            )\n            .toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<int>? readByteList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is int ? e : nullNumber as int).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<int>? readIntList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is int ? e : nullNumber as int).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<int?>? readIntOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is int && e != nullNumber ? e : null).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<double>? readFloatList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is double ? e : nullNumber).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<double?>? readFloatOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is double && e != nullNumber ? e : null).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<int>? readLongList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is int ? e : nullNumber as int).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<int?>? readLongOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is int && e != nullNumber ? e : null).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<double>? readDoubleList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is double ? e : nullNumber).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<double?>? readDoubleOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is double && e != nullNumber ? e : null).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<DateTime>? readDateTimeList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value\n            .map(\n              (e) => e is int && e != nullNumber\n                  ? DateTime.fromMillisecondsSinceEpoch(e, isUtc: true)\n                      .toLocal()\n                  : nullDate,\n            )\n            .toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<DateTime?>? readDateTimeOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value\n            .map(\n              (e) => e is int && e != nullNumber\n                  ? DateTime.fromMillisecondsSinceEpoch(e, isUtc: true)\n                      .toLocal()\n                  : null,\n            )\n            .toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<String>? readStringList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is String ? e : '').toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<String?>? readStringOrNullList(int offset) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) => e is String ? e : null).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<T>? readObjectList<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n    T defaultValue,\n  ) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) {\n            if (e is Object) {\n              final reader = IsarReaderImpl(e);\n              return deserialize(0, reader, allOffsets[T]!, allOffsets);\n            } else {\n              return defaultValue;\n            }\n          }).toList()\n        : null;\n  }\n\n  @tryInline\n  @override\n  List<T?>? readObjectOrNullList<T>(\n    int offset,\n    Deserialize<T> deserialize,\n    Map<Type, List<int>> allOffsets,\n  ) {\n    final value = getProperty<dynamic>(object, offset);\n    return value is List\n        ? value.map((e) {\n            if (e is Object) {\n              final reader = IsarReaderImpl(e);\n              return deserialize(0, reader, allOffsets[T]!, allOffsets);\n            } else {\n              return null;\n            }\n          }).toList()\n        : null;\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/isar_web.dart",
    "content": "// ignore_for_file: unused_field, public_member_api_docs\n\nimport 'dart:async';\n\nimport 'package:isar/isar.dart';\nimport 'package:meta/meta.dart';\n\n/// @nodoc\n@protected\nconst Id isarMinId = -9007199254740990;\n\n/// @nodoc\n@protected\nconst Id isarMaxId = 9007199254740991;\n\n/// @nodoc\n@protected\nconst Id isarAutoIncrementId = -9007199254740991;\n\n/// @nodoc\nNever unsupportedOnWeb() {\n  throw UnsupportedError('This operation is not supported for Isar web');\n}\n\nclass _WebAbi {\n  static const androidArm = null as dynamic;\n  static const androidArm64 = null as dynamic;\n  static const androidIA32 = null as dynamic;\n  static const androidX64 = null as dynamic;\n  static const iosArm64 = null as dynamic;\n  static const iosX64 = null as dynamic;\n  static const linuxArm64 = null as dynamic;\n  static const linuxX64 = null as dynamic;\n  static const macosArm64 = null as dynamic;\n  static const macosX64 = null as dynamic;\n  static const windowsArm64 = null as dynamic;\n  static const windowsX64 = null as dynamic;\n}\n\n/// @nodoc\n@protected\ntypedef IsarAbi = _WebAbi;\n\nFutureOr<void> initializeCoreBinary({\n  Map<IsarAbi, String> libraries = const {},\n  bool download = false,\n}) =>\n    unsupportedOnWeb();\n"
  },
  {
    "path": "packages/isar/lib/src/web/isar_writer_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/web/isar_reader_impl.dart';\nimport 'package:js/js_util.dart';\nimport 'package:meta/dart2js.dart';\n\nclass IsarWriterImpl implements IsarWriter {\n  IsarWriterImpl(this.object);\n\n  final Object object;\n\n  @tryInline\n  @override\n  void writeBool(int offset, bool? value) {\n    final number = value == true\n        ? 1\n        : value == false\n            ? 0\n            : nullNumber;\n    setProperty(object, offset, number);\n  }\n\n  @tryInline\n  @override\n  void writeByte(int offset, int value) {\n    setProperty(object, offset, value);\n  }\n\n  @tryInline\n  @override\n  void writeInt(int offset, int? value) {\n    setProperty(object, offset, value ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeFloat(int offset, double? value) {\n    setProperty(object, offset, value ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeLong(int offset, int? value) {\n    setProperty(object, offset, value ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeDouble(int offset, double? value) {\n    setProperty(object, offset, value ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeDateTime(int offset, DateTime? value) {\n    setProperty(\n      object,\n      offset,\n      value?.toUtc().millisecondsSinceEpoch ?? nullNumber,\n    );\n  }\n\n  @tryInline\n  @override\n  void writeString(int offset, String? value) {\n    setProperty(object, offset, value ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeObject<T>(\n    int offset,\n    Map<Type, List<int>> allOffsets,\n    Serialize<T> serialize,\n    T? value,\n  ) {\n    if (value != null) {\n      final object = newObject<Object>();\n      final writer = IsarWriterImpl(object);\n      serialize(value, writer, allOffsets[T]!, allOffsets);\n      setProperty(this.object, offset, object);\n    }\n  }\n\n  @tryInline\n  @override\n  void writeByteList(int offset, List<int>? values) {\n    setProperty(object, offset, values ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeBoolList(int offset, List<bool?>? values) {\n    final list = values\n        ?.map(\n          (e) => e == false\n              ? 0\n              : e == true\n                  ? 1\n                  : nullNumber,\n        )\n        .toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeIntList(int offset, List<int?>? values) {\n    final list = values?.map((e) => e ?? nullNumber).toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeFloatList(int offset, List<double?>? values) {\n    final list = values?.map((e) => e ?? nullNumber).toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeLongList(int offset, List<int?>? values) {\n    final list = values?.map((e) => e ?? nullNumber).toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeDoubleList(int offset, List<double?>? values) {\n    final list = values?.map((e) => e ?? nullNumber).toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeDateTimeList(int offset, List<DateTime?>? values) {\n    final list = values\n        ?.map((e) => e?.toUtc().millisecondsSinceEpoch ?? nullNumber)\n        .toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeStringList(int offset, List<String?>? values) {\n    final list = values?.map((e) => e ?? nullNumber).toList();\n    setProperty(object, offset, list ?? nullNumber);\n  }\n\n  @tryInline\n  @override\n  void writeObjectList<T>(\n    int offset,\n    Map<Type, List<int>> allOffsets,\n    Serialize<T> serialize,\n    List<T?>? values,\n  ) {\n    if (values != null) {\n      final list = values.map((e) {\n        if (e != null) {\n          final object = newObject<Object>();\n          final writer = IsarWriterImpl(object);\n          serialize(e, writer, allOffsets[T]!, allOffsets);\n          return object;\n        }\n      }).toList();\n      setProperty(object, offset, list);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/open.dart",
    "content": "// ignore_for_file: public_member_api_docs, invalid_use_of_protected_member\n\nimport 'dart:html';\n//import 'dart:js_util';\n\nimport 'package:isar/isar.dart';\n/*import 'package:isar/src/common/schemas.dart';\n\nimport 'package:isar/src/web/bindings.dart';\nimport 'package:isar/src/web/isar_collection_impl.dart';\nimport 'package:isar/src/web/isar_impl.dart';*/\nimport 'package:isar/src/web/isar_web.dart';\nimport 'package:meta/meta.dart';\n\nbool _loaded = false;\nFuture<void> initializeIsarWeb([String? jsUrl]) async {\n  if (_loaded) {\n    return;\n  }\n  _loaded = true;\n\n  final script = ScriptElement();\n  script.type = 'text/javascript';\n  // ignore: unsafe_html\n  script.src = 'https://unpkg.com/isar@${Isar.version}/dist/index.js';\n  script.async = true;\n  document.head!.append(script);\n  await script.onLoad.first.timeout(\n    const Duration(seconds: 30),\n    onTimeout: () {\n      throw IsarError('Failed to load Isar');\n    },\n  );\n}\n\n@visibleForTesting\nvoid doNotInitializeIsarWeb() {\n  _loaded = true;\n}\n\nFuture<Isar> openIsar({\n  required List<CollectionSchema<dynamic>> schemas,\n  String? directory,\n  required String name,\n  required int maxSizeMiB,\n  required bool relaxedDurability,\n  CompactCondition? compactOnLaunch,\n}) async {\n  throw IsarError('Please use Isar 2.5.0 if you need web support. '\n      'A 3.x version with web support will be released soon.');\n  /*await initializeIsarWeb();\n  final schemasJson = getSchemas(schemas).map((e) => e.toJson());\n  final schemasJs = jsify(schemasJson.toList()) as List<dynamic>;\n  final instance = await openIsarJs(name, schemasJs, relaxedDurability)\n      .wait<IsarInstanceJs>();\n  final isar = IsarImpl(name, instance);\n  final cols = <Type, IsarCollection<dynamic>>{};\n  for (final schema in schemas) {\n    final col = instance.getCollection(schema.name);\n    schema.toCollection(<OBJ>() {\n      schema as CollectionSchema<OBJ>;\n      cols[OBJ] = IsarCollectionImpl<OBJ>(\n        isar: isar,\n        native: col,\n        schema: schema,\n      );\n    });\n  }\n\n  isar.attachCollections(cols);\n  return isar;*/\n}\n\nIsar openIsarSync({\n  required List<CollectionSchema<dynamic>> schemas,\n  String? directory,\n  required String name,\n  required int maxSizeMiB,\n  required bool relaxedDurability,\n  CompactCondition? compactOnLaunch,\n}) =>\n    unsupportedOnWeb();\n"
  },
  {
    "path": "packages/isar/lib/src/web/query_build.dart",
    "content": "// ignore_for_file: public_member_api_docs, invalid_use_of_protected_member\n\nimport 'dart:indexed_db';\n\nimport 'package:isar/isar.dart';\n\nimport 'package:isar/src/web/bindings.dart';\nimport 'package:isar/src/web/isar_collection_impl.dart';\nimport 'package:isar/src/web/isar_web.dart';\nimport 'package:isar/src/web/query_impl.dart';\n\nQuery<T> buildWebQuery<T, OBJ>(\n  IsarCollectionImpl<OBJ> col,\n  List<WhereClause> whereClauses,\n  bool whereDistinct,\n  Sort whereSort,\n  FilterOperation? filter,\n  List<SortProperty> sortBy,\n  List<DistinctProperty> distinctBy,\n  int? offset,\n  int? limit,\n  String? property,\n) {\n  final whereClausesJs = whereClauses.map((wc) {\n    if (wc is IdWhereClause) {\n      return _buildIdWhereClause(wc);\n    } else if (wc is IndexWhereClause) {\n      return _buildIndexWhereClause(col.schema, wc);\n    } else {\n      return _buildLinkWhereClause(col, wc as LinkWhereClause);\n    }\n  }).toList();\n\n  final filterJs = filter != null ? _buildFilter(col.schema, filter) : null;\n  final sortJs = sortBy.isNotEmpty ? _buildSort(sortBy) : null;\n  final distinctJs = distinctBy.isNotEmpty ? _buildDistinct(distinctBy) : null;\n\n  final queryJs = QueryJs(\n    col.native,\n    whereClausesJs,\n    whereDistinct,\n    whereSort == Sort.asc,\n    filterJs,\n    sortJs,\n    distinctJs,\n    offset,\n    limit,\n  );\n\n  QueryDeserialize<T> deserialize;\n  //if (property == null) {\n  deserialize = col.deserializeObject as T Function(Object);\n  /*} else {\n    deserialize = (jsObj) => col.schema.deserializeProp(jsObj, property) as T;\n  }*/\n\n  return QueryImpl<T>(col, queryJs, deserialize, property);\n}\n\ndynamic _valueToJs(dynamic value) {\n  if (value == null) {\n    return double.negativeInfinity;\n  } else if (value == true) {\n    return 1;\n  } else if (value == false) {\n    return 0;\n  } else if (value is DateTime) {\n    return value.toUtc().millisecondsSinceEpoch;\n  } else if (value is List) {\n    return value.map(_valueToJs).toList();\n  } else {\n    return value;\n  }\n}\n\nIdWhereClauseJs _buildIdWhereClause(IdWhereClause wc) {\n  return IdWhereClauseJs()\n    ..range = _buildKeyRange(\n      wc.lower,\n      wc.upper,\n      wc.includeLower,\n      wc.includeUpper,\n    );\n}\n\nIndexWhereClauseJs _buildIndexWhereClause(\n  CollectionSchema<dynamic> schema,\n  IndexWhereClause wc,\n) {\n  final index = schema.index(wc.indexName);\n\n  final lower = wc.lower?.toList();\n  final upper = wc.upper?.toList();\n  if (upper != null) {\n    while (index.properties.length > upper.length) {\n      upper.add([]);\n    }\n  }\n\n  dynamic lowerUnwrapped = wc.lower;\n  if (index.properties.length == 1 && lower != null) {\n    lowerUnwrapped = lower.isNotEmpty ? lower[0] : null;\n  }\n\n  dynamic upperUnwrapped = upper;\n  if (index.properties.length == 1 && upper != null) {\n    upperUnwrapped = upper.isNotEmpty ? upper[0] : double.infinity;\n  }\n\n  return IndexWhereClauseJs()\n    ..indexName = wc.indexName\n    ..range = _buildKeyRange(\n      wc.lower != null ? _valueToJs(lowerUnwrapped) : null,\n      wc.upper != null ? _valueToJs(upperUnwrapped) : null,\n      wc.includeLower,\n      wc.includeUpper,\n    );\n}\n\nLinkWhereClauseJs _buildLinkWhereClause(\n  IsarCollectionImpl<dynamic> col,\n  LinkWhereClause wc,\n) {\n  // ignore: unused_local_variable\n  final linkCol = col.isar.getCollectionByNameInternal(wc.linkCollection)!\n      as IsarCollectionImpl;\n  //final backlinkLinkName = linkCol.schema.backlinkLinkNames[wc.linkName];\n  return LinkWhereClauseJs()\n    ..linkCollection = wc.linkCollection\n    //..linkName = backlinkLinkName ?? wc.linkName\n    //..backlink = backlinkLinkName != null\n    ..id = wc.id;\n}\n\nKeyRange? _buildKeyRange(\n  dynamic lower,\n  dynamic upper,\n  bool includeLower,\n  bool includeUpper,\n) {\n  if (lower != null) {\n    if (upper != null) {\n      final boundsEqual = idbCmp(lower, upper) == 0;\n      if (boundsEqual) {\n        if (includeLower && includeUpper) {\n          return KeyRange.only(lower);\n        } else {\n          // empty range\n          return KeyRange.upperBound(double.negativeInfinity, true);\n        }\n      }\n\n      return KeyRange.bound(\n        lower,\n        upper,\n        !includeLower,\n        !includeUpper,\n      );\n    } else {\n      return KeyRange.lowerBound(lower, !includeLower);\n    }\n  } else if (upper != null) {\n    return KeyRange.upperBound(upper, !includeUpper);\n  }\n  return null;\n}\n\nFilterJs? _buildFilter(\n  CollectionSchema<dynamic> schema,\n  FilterOperation filter,\n) {\n  final filterStr = _buildFilterOperation(schema, filter);\n  if (filterStr != null) {\n    return FilterJs('id', 'obj', 'return $filterStr');\n  } else {\n    return null;\n  }\n}\n\nString? _buildFilterOperation(\n  CollectionSchema<dynamic> schema,\n  FilterOperation filter,\n) {\n  if (filter is FilterGroup) {\n    return _buildFilterGroup(schema, filter);\n  } else if (filter is LinkFilter) {\n    unsupportedOnWeb();\n  } else if (filter is FilterCondition) {\n    return _buildCondition(schema, filter);\n  } else {\n    return null;\n  }\n}\n\nString? _buildFilterGroup(CollectionSchema<dynamic> schema, FilterGroup group) {\n  final builtConditions = group.filters\n      .map((op) => _buildFilterOperation(schema, op))\n      .where((e) => e != null)\n      .toList();\n\n  if (builtConditions.isEmpty) {\n    return null;\n  }\n\n  if (group.type == FilterGroupType.not) {\n    return '!(${builtConditions[0]})';\n  } else if (builtConditions.length == 1) {\n    return builtConditions[0];\n  } else if (group.type == FilterGroupType.xor) {\n    final conditions = builtConditions.join(',');\n    return 'IsarQuery.xor($conditions)';\n  } else {\n    final op = group.type == FilterGroupType.or ? '||' : '&&';\n    final condition = builtConditions.join(op);\n    return '($condition)';\n  }\n}\n\nString _buildCondition(\n  CollectionSchema<dynamic> schema,\n  FilterCondition condition,\n) {\n  dynamic _prepareFilterValue(dynamic value) {\n    if (value == null) {\n      return null;\n    } else if (value is String) {\n      return stringify(value);\n    } else {\n      return _valueToJs(value);\n    }\n  }\n\n  final isListOp = condition.type != FilterConditionType.isNull &&\n      condition.type != FilterConditionType.listLength &&\n      schema.property(condition.property).type.isList;\n  final accessor =\n      condition.property == schema.idName ? 'id' : 'obj.${condition.property}';\n  final variable = isListOp ? 'e' : accessor;\n\n  final cond = _buildConditionInternal(\n    conditionType: condition.type,\n    variable: variable,\n    val1: _prepareFilterValue(condition.value1),\n    include1: condition.include1,\n    val2: _prepareFilterValue(condition.value2),\n    include2: condition.include2,\n    caseSensitive: condition.caseSensitive,\n  );\n\n  if (isListOp) {\n    return '(Array.isArray($accessor) && $accessor.some(e => $cond))';\n  } else {\n    return cond;\n  }\n}\n\nString _buildConditionInternal({\n  required FilterConditionType conditionType,\n  required String variable,\n  required Object? val1,\n  required bool include1,\n  required Object? val2,\n  required bool include2,\n  required bool caseSensitive,\n}) {\n  final isNull = '($variable == null || $variable === -Infinity)';\n  switch (conditionType) {\n    case FilterConditionType.equalTo:\n      if (val1 == null) {\n        return isNull;\n      } else if (val1 is String && !caseSensitive) {\n        return '$variable?.toLowerCase() === ${val1.toLowerCase()}';\n      } else {\n        return '$variable === $val1';\n      }\n    case FilterConditionType.between:\n      final val = val1 ?? val2;\n      final lowerOp = include1 ? '>=' : '>';\n      final upperOp = include2 ? '<=' : '<';\n      if (val == null) {\n        return isNull;\n      } else if ((val1 is String?) && (val2 is String?) && !caseSensitive) {\n        final lower = val1?.toLowerCase() ?? '-Infinity';\n        final upper = val2?.toLowerCase() ?? '-Infinity';\n        final variableLc = '$variable?.toLowerCase() ?? -Infinity';\n        final lowerCond = 'indexedDB.cmp($variableLc, $lower) $lowerOp 0';\n        final upperCond = 'indexedDB.cmp($variableLc, $upper) $upperOp 0';\n        return '($lowerCond && $upperCond)';\n      } else {\n        final lowerCond =\n            'indexedDB.cmp($variable, ${val1 ?? '-Infinity'}) $lowerOp 0';\n        final upperCond =\n            'indexedDB.cmp($variable, ${val2 ?? '-Infinity'}) $upperOp 0';\n        return '($lowerCond && $upperCond)';\n      }\n    case FilterConditionType.lessThan:\n      if (val1 == null) {\n        if (include1) {\n          return isNull;\n        } else {\n          return 'false';\n        }\n      } else {\n        final op = include1 ? '<=' : '<';\n        if (val1 is String && !caseSensitive) {\n          return 'indexedDB.cmp($variable?.toLowerCase() ?? '\n              '-Infinity, ${val1.toLowerCase()}) $op 0';\n        } else {\n          return 'indexedDB.cmp($variable, $val1) $op 0';\n        }\n      }\n    case FilterConditionType.greaterThan:\n      if (val1 == null) {\n        if (include1) {\n          return 'true';\n        } else {\n          return '!$isNull';\n        }\n      } else {\n        final op = include1 ? '>=' : '>';\n        if (val1 is String && !caseSensitive) {\n          return 'indexedDB.cmp($variable?.toLowerCase() ?? '\n              '-Infinity, ${val1.toLowerCase()}) $op 0';\n        } else {\n          return 'indexedDB.cmp($variable, $val1) $op 0';\n        }\n      }\n    case FilterConditionType.startsWith:\n    case FilterConditionType.endsWith:\n    case FilterConditionType.contains:\n      final op = conditionType == FilterConditionType.startsWith\n          ? 'startsWith'\n          : conditionType == FilterConditionType.endsWith\n              ? 'endsWith'\n              : 'includes';\n      if (val1 is String) {\n        final isString = 'typeof $variable == \"string\"';\n        if (!caseSensitive) {\n          return '($isString && $variable.toLowerCase() '\n              '.$op(${val1.toLowerCase()}))';\n        } else {\n          return '($isString && $variable.$op($val1))';\n        }\n      } else {\n        throw IsarError('Unsupported type for condition');\n      }\n    case FilterConditionType.matches:\n      throw UnimplementedError();\n    case FilterConditionType.isNull:\n      return isNull;\n    // ignore: no_default_cases\n    default:\n      throw UnimplementedError();\n  }\n}\n\nSortCmpJs _buildSort(List<SortProperty> properties) {\n  final sort = properties.map((e) {\n    final op = e.sort == Sort.asc ? '' : '-';\n    return '${op}indexedDB.cmp(a.${e.property} ?? \"-Infinity\", b.${e.property} '\n        '?? \"-Infinity\")';\n  }).join('||');\n  return SortCmpJs('a', 'b', 'return $sort');\n}\n\nDistinctValueJs _buildDistinct(List<DistinctProperty> properties) {\n  final distinct = properties.map((e) {\n    if (e.caseSensitive == false) {\n      return 'obj.${e.property}?.toLowerCase() ?? \"-Infinity\"';\n    } else {\n      return 'obj.${e.property}?.toString() ?? \"-Infinity\"';\n    }\n  }).join('+');\n  return DistinctValueJs('obj', 'return $distinct');\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/query_impl.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:js';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/web/bindings.dart';\n\nimport 'package:isar/src/web/isar_collection_impl.dart';\nimport 'package:isar/src/web/isar_web.dart';\n\ntypedef QueryDeserialize<T> = T Function(Object);\n\nclass QueryImpl<T> extends Query<T> {\n  QueryImpl(this.col, this.queryJs, this.deserialize, this.propertyName);\n  final IsarCollectionImpl<dynamic> col;\n  final QueryJs queryJs;\n  final QueryDeserialize<T> deserialize;\n  final String? propertyName;\n\n  @override\n  Isar get isar => col.isar;\n\n  @override\n  Future<T?> findFirst() {\n    return col.isar.getTxn(false, (IsarTxnJs txn) async {\n      final result = await queryJs.findFirst(txn).wait<Object?>();\n      if (result == null) {\n        return null;\n      }\n      return deserialize(result);\n    });\n  }\n\n  @override\n  T? findFirstSync() => unsupportedOnWeb();\n\n  @override\n  Future<List<T>> findAll() {\n    return col.isar.getTxn(false, (IsarTxnJs txn) async {\n      final result = await queryJs.findAll(txn).wait<List<dynamic>>();\n      return result.map((e) => deserialize(e as Object)).toList();\n    });\n  }\n\n  @override\n  List<T> findAllSync() => unsupportedOnWeb();\n\n  @override\n  Future<R?> aggregate<R>(AggregationOp op) {\n    return col.isar.getTxn(false, (IsarTxnJs txn) async {\n      final property = propertyName ?? col.schema.idName;\n\n      num? result;\n      switch (op) {\n        case AggregationOp.min:\n          result = await queryJs.min(txn, property).wait();\n          break;\n        case AggregationOp.max:\n          result = await queryJs.max(txn, property).wait();\n          break;\n        case AggregationOp.sum:\n          result = await queryJs.sum(txn, property).wait();\n          break;\n        case AggregationOp.average:\n          result = await queryJs.average(txn, property).wait();\n          break;\n        case AggregationOp.count:\n          result = await queryJs.count(txn).wait();\n          break;\n        // ignore: no_default_cases\n        default:\n          throw UnimplementedError();\n      }\n\n      if (result == null) {\n        return null;\n      }\n\n      if (R == DateTime) {\n        return DateTime.fromMillisecondsSinceEpoch(result.toInt()).toLocal()\n            as R;\n      } else if (R == int) {\n        return result.toInt() as R;\n      } else if (R == double) {\n        return result.toDouble() as R;\n      } else {\n        return null;\n      }\n    });\n  }\n\n  @override\n  R? aggregateSync<R>(AggregationOp op) => unsupportedOnWeb();\n\n  @override\n  Future<bool> deleteFirst() {\n    return col.isar.getTxn(true, (IsarTxnJs txn) {\n      return queryJs.deleteFirst(txn).wait();\n    });\n  }\n\n  @override\n  bool deleteFirstSync() => unsupportedOnWeb();\n\n  @override\n  Future<int> deleteAll() {\n    return col.isar.getTxn(true, (IsarTxnJs txn) {\n      return queryJs.deleteAll(txn).wait();\n    });\n  }\n\n  @override\n  int deleteAllSync() => unsupportedOnWeb();\n\n  @override\n  Stream<List<T>> watch({bool fireImmediately = false}) {\n    JsFunction? stop;\n    final controller = StreamController<List<T>>(\n      onCancel: () {\n        stop?.apply([]);\n      },\n    );\n\n    if (fireImmediately) {\n      findAll().then(controller.add);\n    }\n\n    final Null Function(List<dynamic> results) callback =\n        allowInterop((List<dynamic> results) {\n      controller.add(results.map((e) => deserialize(e as Object)).toList());\n    });\n    stop = col.native.watchQuery(queryJs, callback);\n\n    return controller.stream;\n  }\n\n  @override\n  Stream<void> watchLazy({bool fireImmediately = false}) {\n    JsFunction? stop;\n    final controller = StreamController<void>(\n      onCancel: () {\n        stop?.apply([]);\n      },\n    );\n\n    final Null Function() callback = allowInterop(() {\n      controller.add(null);\n    });\n    stop = col.native.watchQueryLazy(queryJs, callback);\n\n    return controller.stream;\n  }\n\n  @override\n  Future<R> exportJsonRaw<R>(R Function(Uint8List) callback) async {\n    return col.isar.getTxn(false, (IsarTxnJs txn) async {\n      final result = await queryJs.findAll(txn).wait<dynamic>();\n      final jsonStr = stringify(result);\n      return callback(const Utf8Encoder().convert(jsonStr));\n    });\n  }\n\n  @override\n  Future<List<Map<String, dynamic>>> exportJson() {\n    return col.isar.getTxn(false, (IsarTxnJs txn) async {\n      final result = await queryJs.findAll(txn).wait<List<dynamic>>();\n      return result.map((e) => jsMapToDart(e as Object)).toList();\n    });\n  }\n\n  @override\n  R exportJsonRawSync<R>(R Function(Uint8List) callback) => unsupportedOnWeb();\n\n  @override\n  List<Map<String, dynamic>> exportJsonSync({bool primitiveNull = true}) =>\n      unsupportedOnWeb();\n}\n"
  },
  {
    "path": "packages/isar/lib/src/web/split_words.dart",
    "content": "// ignore_for_file: public_member_api_docs\n\nimport 'package:isar/src/web/isar_web.dart';\n\nList<String> isarSplitWords(String input) => unsupportedOnWeb();\n"
  },
  {
    "path": "packages/isar/pubspec.yaml",
    "content": "name: isar\ndescription: Extremely fast, easy to use, and fully async NoSQL database for Flutter.\nversion: 3.1.8\nrepository: https://github.com/isar-community/isar/tree/main/packages/isar\nhomepage: https://github.com/isar-community/isar\nissue_tracker: https://github.com/isar-community/isar/issues\ndocumentation: https://isar.dev\npublish_to: https://pub.isar-community.dev/\nfunding:\n  - https://github.com/sponsors/leisim/\n\nenvironment:\n  sdk: \">=2.17.0 <3.0.0\"\n\ndependencies:\n  ffi: \">=2.0.0 <3.0.0\"\n#  isar_test:\n#    path: ../isar_test\n  js: any\n  meta: ^1.7.0\n\ndev_dependencies:\n  ffigen: \"^7.0.0\"\n  test: ^1.21.1\n  very_good_analysis: ^3.0.1\n"
  },
  {
    "path": "packages/isar/test/isar_reader_writer_test.dart",
    "content": "@TestOn('vm')\n\n// ignore_for_file: constant_identifier_names\n\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/native/isar_core.dart';\nimport 'package:isar/src/native/isar_reader_impl.dart';\nimport 'package:isar/src/native/isar_writer_impl.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('Golden Binary', () {\n    late final json =\n        File('../isar_core/tests/binary_golden.json').readAsStringSync();\n    late final tests = (jsonDecode(json) as List<dynamic>)\n        .map((e) => BinaryTest.fromJson(e as Map<String, dynamic>))\n        .toList();\n\n    test('IsarReader', () {\n      var t = 0;\n      for (final test in tests) {\n        final reader = IsarReaderImpl(Uint8List.fromList(test.bytes));\n        var offset = 2;\n        for (var i = 0; i < test.types.length; i++) {\n          final type = test.types[i];\n          final nullableValue = type.read(reader, offset, true);\n          expect(nullableValue, test.values[i], reason: '${test.types} $t');\n\n          final nonNullableValue = type.read(reader, offset, false);\n          _expectIgnoreNull(nonNullableValue, test.values[i], type);\n          offset += type.size;\n        }\n        t++;\n      }\n    });\n\n    test('IsarWriter', () {\n      for (final test in tests) {\n        final buffer = Uint8List(10000);\n        final size =\n            test.types.fold<int>(0, (sum, type) => sum + type.size) + 2;\n\n        final bufferView = buffer.buffer.asUint8List(0, test.bytes.length);\n        final writer = IsarWriterImpl(bufferView, size);\n        var offset = 2;\n        for (var i = 0; i < test.types.length; i++) {\n          final type = test.types[i];\n          final value = test.values[i];\n          type.write(writer, offset, value);\n          offset += type.size;\n        }\n\n        expect(buffer.sublist(0, test.bytes.length), test.bytes);\n      }\n    });\n  });\n}\n\nenum Type {\n  Bool(1, false, _readBool, _writeBool),\n  Byte(1, 0, _readByte, _writeByte),\n  Int(4, nullInt, _readInt, _writeInt),\n  Float(4, nullFloat, _readFloat, _writeFloat),\n  Long(8, nullLong, _readLong, _writeLong),\n  Double(8, nullDouble, _readDouble, _writeDouble),\n  String(3, '', _readString, _writeString),\n  BoolList(3, false, _readBoolList, _writeBoolList),\n  ByteList(3, 0, _readByteList, _writeByteList),\n  IntList(3, nullInt, _readIntList, _writeIntList),\n  FloatList(3, nullFloat, _readFloatList, _writeFloatList),\n  LongList(3, nullLong, _readLongList, _writeLongList),\n  DoubleList(3, nullDouble, _readDoubleList, _writeDoubleList),\n  StringList(3, '', _readStringList, _writeStringList);\n\n  const Type(this.size, this.nullValue, this.read, this.write);\n\n  final int size;\n  final dynamic nullValue;\n  final dynamic Function(IsarReader reader, int offset, bool nullable) read;\n  final void Function(IsarWriter reader, int offset, dynamic value) write;\n}\n\nclass BinaryTest {\n  const BinaryTest(this.types, this.values, this.bytes);\n\n  factory BinaryTest.fromJson(Map<String, dynamic> json) {\n    return BinaryTest(\n      (json['types'] as List)\n          .map((type) => Type.values.firstWhere((t) => t.name == type))\n          .toList(),\n      json['values'] as List,\n      (json['bytes'] as List).cast(),\n    );\n  }\n\n  final List<Type> types;\n  final List<dynamic> values;\n  final List<int> bytes;\n}\n\nvoid _expectIgnoreNull(\n  dynamic left,\n  dynamic right,\n  Type type, {\n  bool inList = false,\n}) {\n  if (right == null && (type.index < Type.BoolList.index || inList)) {\n    if (left is double) {\n      expect(left, isNaN);\n    } else {\n      expect(left, type.nullValue);\n    }\n  } else if (right is List) {\n    left as List;\n    for (var i = 0; i < right.length; i++) {\n      _expectIgnoreNull(left[i], right[i], type, inList: true);\n    }\n  } else {\n    expect(left, right);\n  }\n}\n\nbool? _readBool(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readBoolOrNull(offset);\n  } else {\n    return reader.readBool(offset);\n  }\n}\n\nvoid _writeBool(IsarWriter writer, int offset, dynamic value) {\n  writer.writeBool(offset, value as bool?);\n}\n\nint? _readByte(IsarReader reader, int offset, bool nullable) {\n  return reader.readByte(offset);\n}\n\nvoid _writeByte(IsarWriter writer, int offset, dynamic value) {\n  writer.writeByte(offset, value as int);\n}\n\nint? _readInt(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readIntOrNull(offset);\n  } else {\n    return reader.readInt(offset);\n  }\n}\n\nvoid _writeInt(IsarWriter writer, int offset, dynamic value) {\n  writer.writeInt(offset, value as int?);\n}\n\ndouble? _readFloat(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readFloatOrNull(offset);\n  } else {\n    return reader.readFloat(offset);\n  }\n}\n\nvoid _writeFloat(IsarWriter writer, int offset, dynamic value) {\n  writer.writeFloat(offset, value as double?);\n}\n\nint? _readLong(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readLongOrNull(offset);\n  } else {\n    return reader.readLong(offset);\n  }\n}\n\nvoid _writeLong(IsarWriter writer, int offset, dynamic value) {\n  writer.writeLong(offset, value as int?);\n}\n\ndouble? _readDouble(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readDoubleOrNull(offset);\n  } else {\n    return reader.readDouble(offset);\n  }\n}\n\nvoid _writeDouble(IsarWriter writer, int offset, dynamic value) {\n  writer.writeDouble(offset, value as double?);\n}\n\nString? _readString(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readStringOrNull(offset);\n  } else {\n    return reader.readString(offset);\n  }\n}\n\nvoid _writeString(IsarWriter writer, int offset, dynamic value) {\n  final bytes = value is String ? utf8.encode(value) as Uint8List : null;\n  writer.writeByteList(offset, bytes);\n}\n\nList<bool?>? _readBoolList(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readBoolOrNullList(offset);\n  } else {\n    return reader.readBoolList(offset);\n  }\n}\n\nvoid _writeBoolList(IsarWriter writer, int offset, dynamic value) {\n  writer.writeBoolList(offset, (value as List?)?.cast());\n}\n\nList<int>? _readByteList(IsarReader reader, int offset, bool nullable) {\n  return reader.readByteList(offset);\n}\n\nvoid _writeByteList(IsarWriter writer, int offset, dynamic value) {\n  final bytes = value is List ? Uint8List.fromList(value.cast()) : null;\n  writer.writeByteList(offset, bytes);\n}\n\nList<int?>? _readIntList(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readIntOrNullList(offset);\n  } else {\n    return reader.readIntList(offset);\n  }\n}\n\nvoid _writeIntList(IsarWriter writer, int offset, dynamic value) {\n  writer.writeIntList(offset, (value as List?)?.cast());\n}\n\nList<double?>? _readFloatList(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readFloatOrNullList(offset);\n  } else {\n    return reader.readFloatList(offset);\n  }\n}\n\nvoid _writeFloatList(IsarWriter writer, int offset, dynamic value) {\n  writer.writeFloatList(offset, (value as List?)?.cast());\n}\n\nList<int?>? _readLongList(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readLongOrNullList(offset);\n  } else {\n    return reader.readLongList(offset);\n  }\n}\n\nvoid _writeLongList(IsarWriter writer, int offset, dynamic value) {\n  writer.writeLongList(offset, (value as List?)?.cast());\n}\n\nList<double?>? _readDoubleList(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readDoubleOrNullList(offset);\n  } else {\n    return reader.readDoubleList(offset);\n  }\n}\n\nvoid _writeDoubleList(IsarWriter writer, int offset, dynamic value) {\n  writer.writeDoubleList(offset, (value as List?)?.cast());\n}\n\nList<String?>? _readStringList(IsarReader reader, int offset, bool nullable) {\n  if (nullable) {\n    return reader.readStringOrNullList(offset);\n  } else {\n    return reader.readStringList(offset);\n  }\n}\n\nvoid _writeStringList(IsarWriter writer, int offset, dynamic value) {\n  writer.writeStringList(offset, (value as List?)?.cast());\n}\n"
  },
  {
    "path": "packages/isar/tool/get_version.dart",
    "content": "import 'package:isar/isar.dart';\n\nvoid main() {\n  // ignore: avoid_print\n  print(Isar.version);\n}\n"
  },
  {
    "path": "packages/isar/tool/verify_release_version.dart",
    "content": "import 'package:isar/isar.dart';\n\nvoid main(List<String> args) {\n  if (Isar.version != args[0]) {\n    throw StateError(\n      'Invalid Isar version for release: ${Isar.version} != ${args[0]}',\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_core/Cargo.toml",
    "content": "[package]\nname = \"isar-core\"\nversion = \"0.0.0\"\nauthors = [\"Simon Leier <simonleier@gmail.com>\"]\nedition = \"2021\"\n\n[dependencies]\nitertools = \"0.10.3\"\nenum_dispatch = \"0.3.8\"\nffi = { package = \"mdbx-sys\", path = \"../mdbx_sys\" }\nlibc = \"0.2\"\nxxhash-rust = { version = \"0.8.5\", features = [\"xxh3\"] }\nserde =  { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nonce_cell = \"1.10.0\"\ncrossbeam-channel = \"0.5.4\"\nbyteorder = \"1\"\npaste = \"1.0\"\nintmap = \"2.0.0\"\nsnafu = \"0.7.0\"\n\n[target.'cfg(target_os = \"windows\")'.dependencies]\nwidestring = \"1.0\"\n\n[dev-dependencies]\nrand = \"0.8.5\"\ncfg-if = \"1\"\nfloat_next_after = \"0.1\"\n\n[dev-dependencies.serde_json]\nversion = \"*\"\nfeatures = [\"float_roundtrip\"]\n"
  },
  {
    "path": "packages/isar_core/README.md",
    "content": "# isar-core\n\nThe Rust core of the Isar database."
  },
  {
    "path": "packages/isar_core/src/collection.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::{illegal_arg, IsarError, Result};\nuse crate::index::index_key::IndexKey;\nuse crate::index::index_key_builder::IndexKeyBuilder;\nuse crate::index::IsarIndex;\nuse crate::link::IsarLink;\nuse crate::mdbx::db::Db;\nuse crate::object::id::BytesToId;\nuse crate::object::isar_object::IsarObject;\nuse crate::object::json_encode_decode::JsonEncodeDecode;\nuse crate::object::object_builder::ObjectBuilder;\nuse crate::object::property::Property;\nuse crate::query::query_builder::QueryBuilder;\nuse crate::txn::IsarTxn;\nuse crate::watch::change_set::ChangeSet;\nuse intmap::IntMap;\nuse itertools::Itertools;\nuse serde_json::Value;\nuse std::cell::Cell;\nuse std::ops::Deref;\nuse xxhash_rust::xxh3::xxh3_64;\n\npub struct IsarCollection {\n    pub name: String,\n    pub id: u64,\n\n    pub properties: Vec<Property>,\n    pub embedded_properties: IntMap<Vec<Property>>,\n\n    pub(crate) instance_id: u64,\n    pub(crate) db: Db,\n\n    pub(crate) indexes: Vec<IsarIndex>,\n    pub(crate) links: Vec<IsarLink>, // links from this collection\n    backlinks: Vec<IsarLink>,        // links to this collection\n\n    auto_increment: Cell<i64>,\n}\n\nunsafe impl Send for IsarCollection {}\nunsafe impl Sync for IsarCollection {}\n\nimpl IsarCollection {\n    #[allow(clippy::too_many_arguments)]\n    pub(crate) fn new(\n        db: Db,\n        instance_id: u64,\n        name: &str,\n        properties: Vec<Property>,\n        embedded_properties: IntMap<Vec<Property>>,\n        indexes: Vec<IsarIndex>,\n        links: Vec<IsarLink>,\n        backlinks: Vec<IsarLink>,\n    ) -> Self {\n        let id = xxh3_64(name.as_bytes());\n        IsarCollection {\n            name: name.to_string(),\n            id,\n            properties,\n            embedded_properties,\n            instance_id,\n            db,\n            indexes,\n            links,\n            backlinks,\n            auto_increment: Cell::new(0),\n        }\n    }\n\n    pub fn new_object_builder(&self, buffer: Option<Vec<u8>>) -> ObjectBuilder {\n        ObjectBuilder::new(&self.properties, buffer)\n    }\n\n    pub fn new_query_builder(&self) -> QueryBuilder {\n        QueryBuilder::new(self)\n    }\n\n    pub(crate) fn init_auto_increment(&self, cursors: &IsarCursors) -> Result<()> {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        if let Some((key, _)) = cursor.move_to_last()? {\n            let id = key.deref().to_id();\n            self.update_auto_increment(id);\n        }\n        Ok(())\n    }\n\n    pub(crate) fn update_auto_increment(&self, id: i64) {\n        if id > self.auto_increment.get() {\n            self.auto_increment.set(id);\n        }\n    }\n\n    pub fn auto_increment(&self, _: &mut IsarTxn) -> Result<i64> {\n        self.auto_increment_internal()\n    }\n\n    pub(crate) fn auto_increment_internal(&self) -> Result<i64> {\n        let last = self.auto_increment.get();\n        if last < i64::MAX {\n            self.auto_increment.set(last + 1);\n            Ok(last + 1)\n        } else {\n            Err(IsarError::AutoIncrementOverflow {})\n        }\n    }\n\n    pub fn get<'txn>(&self, txn: &'txn mut IsarTxn, id: i64) -> Result<Option<IsarObject<'txn>>> {\n        txn.read(self.instance_id, |cursors| {\n            let mut cursor = cursors.get_cursor(self.db)?;\n            let object = cursor\n                .move_to(&id)?\n                .map(|(_, v)| IsarObject::from_bytes(&v));\n            Ok(object)\n        })\n    }\n\n    pub(crate) fn get_index_by_id(&self, index_id: u64) -> Result<&IsarIndex> {\n        self.indexes\n            .iter()\n            .find(|i| i.id == index_id)\n            .ok_or(IsarError::UnknownIndex {})\n    }\n\n    pub fn get_by_index<'txn>(\n        &self,\n        txn: &'txn mut IsarTxn,\n        index_id: u64,\n        key: &IndexKey,\n    ) -> Result<Option<(i64, IsarObject<'txn>)>> {\n        let index = self.get_index_by_id(index_id)?;\n        txn.read(self.instance_id, |cursors| {\n            if let Some(id) = index.get_id(cursors, key)? {\n                let mut cursor = cursors.get_cursor(self.db)?;\n                let (_, bytes) = cursor.move_to(&id)?.ok_or(IsarError::DbCorrupted {\n                    message: \"Invalid index entry\".to_string(),\n                })?;\n                let result = (id, IsarObject::from_bytes(&bytes));\n                Ok(Some(result))\n            } else {\n                Ok(None)\n            }\n        })\n    }\n\n    pub fn put(&self, txn: &mut IsarTxn, id: Option<i64>, object: IsarObject) -> Result<i64> {\n        txn.write(self.instance_id, |cursors, change_set| {\n            self.put_internal(cursors, change_set, id, object)\n        })\n    }\n\n    pub fn put_by_index(\n        &self,\n        txn: &mut IsarTxn,\n        index_id: u64,\n        object: IsarObject,\n    ) -> Result<i64> {\n        let index = self.get_index_by_id(index_id)?;\n        if index.multi_entry {\n            illegal_arg(\"Cannot put by a multi-entry index\")?;\n        }\n        let key_builder = IndexKeyBuilder::new(&index.properties);\n        txn.write(self.instance_id, |cursors, change_set| {\n            let key = key_builder.create_primitive_key(object);\n            let id = index.get_id(cursors, &key)?;\n            let new_id = self.put_internal(cursors, change_set, id, object)?;\n            Ok(new_id)\n        })\n    }\n\n    fn put_internal(\n        &self,\n        cursors: &IsarCursors,\n        mut change_set: Option<&mut ChangeSet>,\n        id: Option<i64>,\n        object: IsarObject,\n    ) -> Result<i64> {\n        if object.len() > IsarObject::MAX_SIZE as usize {\n            illegal_arg(\"Object is bigger than 16MB\")?;\n        }\n\n        let id = if let Some(id) = id {\n            self.delete_internal(cursors, false, change_set.as_deref_mut(), id)?;\n            self.update_auto_increment(id);\n            id\n        } else {\n            self.auto_increment_internal()?\n        };\n\n        for index in &self.indexes {\n            index.create_for_object(cursors, id, object, |id| {\n                self.delete_internal(cursors, true, change_set.as_deref_mut(), id)?;\n                Ok(())\n            })?;\n        }\n\n        let mut cursor = cursors.get_cursor(self.db)?;\n        cursor.put(&id, object.as_bytes())?;\n        if let Some(change_set) = change_set {\n            change_set.register_change(self.id, id, object);\n        }\n        Ok(id)\n    }\n\n    pub fn delete(&self, txn: &mut IsarTxn, id: i64) -> Result<bool> {\n        txn.write(self.instance_id, |cursors, change_set| {\n            self.delete_internal(cursors, true, change_set, id)\n        })\n    }\n\n    pub fn delete_by_index(\n        &self,\n        txn: &mut IsarTxn,\n        index_id: u64,\n        key: &IndexKey,\n    ) -> Result<bool> {\n        let index = self.get_index_by_id(index_id)?;\n        txn.write(self.instance_id, |cursors, change_set| {\n            if let Some(id) = index.get_id(cursors, key)? {\n                self.delete_internal(cursors, true, change_set, id)?;\n                Ok(true)\n            } else {\n                Ok(false)\n            }\n        })\n    }\n\n    fn delete_internal(\n        &self,\n        cursors: &IsarCursors,\n        delete_links: bool,\n        change_set: Option<&mut ChangeSet>,\n        id: i64,\n    ) -> Result<bool> {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        if let Some((_, object)) = cursor.move_to(&id)? {\n            let object = IsarObject::from_bytes(&object);\n            for index in &self.indexes {\n                index.delete_for_object(cursors, id, object)?;\n            }\n            if delete_links {\n                for link in &self.links {\n                    link.delete_all_for_object(cursors, id)?;\n                }\n                for link in &self.backlinks {\n                    link.delete_all_for_object(cursors, id)?;\n                }\n            }\n            if let Some(change_set) = change_set {\n                change_set.register_change(self.id, id, object);\n            }\n            cursor.delete_current()?;\n            Ok(true)\n        } else {\n            Ok(false)\n        }\n    }\n\n    pub(crate) fn get_link_backlink(&self, link_id: u64) -> Result<&IsarLink> {\n        if let Some(link) = self.links.iter().find(|l| l.id == link_id) {\n            Ok(link)\n        } else if let Some(link) = self.backlinks.iter().find(|l| l.id == link_id) {\n            Ok(link)\n        } else {\n            illegal_arg(\"IsarLink does not exist\")\n        }\n    }\n\n    pub fn link(&self, txn: &mut IsarTxn, link_id: u64, id: i64, target_id: i64) -> Result<bool> {\n        let link = self.get_link_backlink(link_id)?;\n        txn.write(self.instance_id, |cursors, _| {\n            link.create(cursors, id, target_id)\n        })\n    }\n\n    pub fn unlink(&self, txn: &mut IsarTxn, link_id: u64, id: i64, target_id: i64) -> Result<bool> {\n        let link = self.get_link_backlink(link_id)?;\n        txn.write(self.instance_id, |cursors, _| {\n            link.delete(cursors, id, target_id)\n        })\n    }\n\n    pub fn unlink_all(&self, txn: &mut IsarTxn, link_id: u64, id: i64) -> Result<()> {\n        let link = self.get_link_backlink(link_id)?;\n        txn.write(self.instance_id, |cursors, _| {\n            link.delete_all_for_object(cursors, id)\n        })\n    }\n\n    pub fn clear(&self, txn: &mut IsarTxn) -> Result<()> {\n        txn.write(self.instance_id, |cursors, change_set| {\n            for index in &self.indexes {\n                index.clear(cursors)?;\n            }\n            for link in &self.links {\n                link.clear(cursors)?;\n            }\n            for link in &self.backlinks {\n                link.clear(cursors)?;\n            }\n            cursors.clear_db(self.db)?;\n            self.auto_increment.set(0);\n\n            if let Some(change_set) = change_set {\n                change_set.register_all(self.id);\n            }\n\n            Ok(())\n        })\n    }\n\n    pub fn count(&self, txn: &mut IsarTxn) -> Result<u64> {\n        txn.read(self.instance_id, |cursors| Ok(cursors.db_stat(self.db)?.0))\n    }\n\n    pub fn get_size(\n        &self,\n        txn: &mut IsarTxn,\n        include_indexes: bool,\n        include_links: bool,\n    ) -> Result<u64> {\n        txn.read(self.instance_id, |cursors| {\n            let mut size = cursors.db_stat(self.db)?.1;\n\n            if include_indexes {\n                for index in &self.indexes {\n                    size += index.get_size(cursors)?;\n                }\n            }\n\n            if include_links {\n                for link in &self.links {\n                    size += link.get_size(cursors)?;\n                }\n            }\n\n            Ok(size)\n        })\n    }\n\n    pub fn import_json(&self, txn: &mut IsarTxn, id_name: Option<&str>, json: Value) -> Result<()> {\n        txn.write(self.instance_id, |cursors, mut change_set| {\n            let array = json.as_array().ok_or(IsarError::InvalidJson {})?;\n            let mut ob_result_cache = None;\n            for value in array {\n                let id = if let Some(id_name) = id_name {\n                    if let Some(id) = value.get(id_name) {\n                        let id = id.as_i64().ok_or(IsarError::InvalidJson {})?;\n                        Some(id)\n                    } else {\n                        None\n                    }\n                } else {\n                    None\n                };\n\n                let mut ob = ObjectBuilder::new(&self.properties, ob_result_cache);\n                JsonEncodeDecode::decode(\n                    &self.properties,\n                    &self.embedded_properties,\n                    &mut ob,\n                    value,\n                )?;\n                let object = ob.finish();\n                self.put_internal(cursors, change_set.as_deref_mut(), id, object)?;\n                ob_result_cache = Some(ob.recycle());\n            }\n            Ok(())\n        })\n    }\n\n    pub(crate) fn fill_indexes(&self, index_ids: &[u64], cursors: &IsarCursors) -> Result<()> {\n        let indexes = index_ids\n            .iter()\n            .map(|id| self.get_index_by_id(*id).unwrap())\n            .collect_vec();\n\n        let mut cursor = cursors.get_cursor(self.db)?;\n        cursor.iter_all(false, true, |cursor, id_bytes, object| {\n            let id = id_bytes.to_id();\n\n            // The object might become invalid if another one is deleted by an index. TODO: Find a better solution\n            let bytes = object.to_vec();\n            let object = IsarObject::from_bytes(&bytes);\n\n            for index in &indexes {\n                index.create_for_object(cursors, id, object, |id| {\n                    let deleted = self.delete_internal(cursors, true, None, id)?;\n                    if deleted {\n                        cursor.move_to_next()?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Ok(true)\n        })?;\n        Ok(())\n    }\n\n    pub fn verify(&self, txn: &mut IsarTxn, objects: &IntMap<IsarObject>) -> Result<()> {\n        txn.read(self.instance_id, |cursors| {\n            let mut counter = 0;\n            let mut cursor = cursors.get_cursor(self.db)?;\n            cursor.iter_all(false, true, |_, id_bytes, bytes| {\n                let id = id_bytes.to_id();\n                let db_object = IsarObject::from_bytes(bytes);\n                let db_json = JsonEncodeDecode::encode(\n                    &self.properties,\n                    &self.embedded_properties,\n                    db_object,\n                    false,\n                );\n                if let Some(object) = objects.get(id as u64) {\n                    let json = JsonEncodeDecode::encode(\n                        &self.properties,\n                        &self.embedded_properties,\n                        *object,\n                        false,\n                    );\n                    if json == db_json {\n                        counter += 1;\n                        return Ok(true);\n                    }\n                }\n                Err(IsarError::DbCorrupted {\n                    message: \"Unknown object in database.\".to_string(),\n                })\n            })?;\n\n            if counter != objects.len() {\n                return Err(IsarError::DbCorrupted {\n                    message: \"Object missing in database.\".to_string(),\n                });\n            }\n\n            for index in &self.indexes {\n                index.verify(cursors, objects)?;\n            }\n\n            Ok(())\n        })\n    }\n\n    pub fn verify_link(&self, txn: &mut IsarTxn, link_id: u64, links: &[(i64, i64)]) -> Result<()> {\n        let link = self.get_link_backlink(link_id)?;\n        txn.read(self.instance_id, |cursors| link.verify(cursors, links))\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/cursor.rs",
    "content": "use crate::error::Result;\nuse crate::mdbx::cursor::{Cursor, UnboundCursor};\nuse crate::mdbx::db::Db;\nuse crate::mdbx::txn::Txn;\nuse intmap::IntMap;\nuse std::cell::RefCell;\nuse std::ops::{Deref, DerefMut};\n\npub(crate) struct IsarCursors<'txn, 'env> {\n    txn: &'txn Txn<'env>,\n    unbound_cursors: RefCell<Vec<UnboundCursor>>,\n    cursors: RefCell<IntMap<Cursor<'txn>>>,\n}\n\nimpl<'txn, 'env> IsarCursors<'txn, 'env> {\n    pub fn new(\n        txn: &'txn Txn<'env>,\n        unbound_cursors: Vec<UnboundCursor>,\n    ) -> IsarCursors<'txn, 'env> {\n        IsarCursors {\n            txn,\n            unbound_cursors: RefCell::new(unbound_cursors),\n            cursors: RefCell::new(IntMap::new()),\n        }\n    }\n\n    pub fn get_cursor<'a>(&'a self, db: Db) -> Result<IsarCursor<'a, 'txn, 'env>> {\n        let cursor = if let Some(cursor) = self.cursors.borrow_mut().remove(db.runtime_id()) {\n            cursor\n        } else {\n            let unbound = self\n                .unbound_cursors\n                .borrow_mut()\n                .pop()\n                .unwrap_or_else(UnboundCursor::new);\n            unbound.bind(self.txn, db)?\n        };\n\n        Ok(IsarCursor {\n            cursors: self,\n            cursor: Some(cursor),\n            db_id: db.runtime_id(),\n        })\n    }\n\n    pub fn db_stat(&self, db: Db) -> Result<(u64, u64)> {\n        db.stat(&self.txn)\n    }\n\n    pub fn clear_db(&self, db: Db) -> Result<()> {\n        db.clear(&self.txn)\n    }\n\n    pub fn close(self) -> Vec<UnboundCursor> {\n        let mut unbound_cursors = self.unbound_cursors.take();\n        for (_, cursor) in self.cursors.borrow_mut().drain() {\n            unbound_cursors.push(cursor.unbind())\n        }\n        unbound_cursors\n    }\n}\n\npub(crate) struct IsarCursor<'a, 'txn, 'env> {\n    cursors: &'a IsarCursors<'txn, 'env>,\n    cursor: Option<Cursor<'txn>>,\n    db_id: u64,\n}\n\nimpl<'a, 'txn, 'env> Deref for IsarCursor<'a, 'txn, 'env> {\n    type Target = Cursor<'txn>;\n\n    fn deref(&self) -> &Self::Target {\n        self.cursor.as_ref().unwrap()\n    }\n}\n\nimpl<'a, 'txn, 'env> DerefMut for IsarCursor<'a, 'txn, 'env> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.cursor.as_mut().unwrap()\n    }\n}\n\nimpl<'a, 'txn, 'env> Drop for IsarCursor<'a, 'txn, 'env> {\n    fn drop(&mut self) {\n        let cursor = self.cursor.take().unwrap();\n        let cursors = &self.cursors.cursors;\n        if !cursors.borrow().contains_key(self.db_id) {\n            cursors.borrow_mut().insert(self.db_id, cursor);\n        } else if self.cursors.unbound_cursors.borrow().len() < 3 {\n            self.cursors\n                .unbound_cursors\n                .borrow_mut()\n                .push(cursor.unbind());\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/error.rs",
    "content": "use snafu::Snafu;\n\npub type Result<T> = std::result::Result<T, IsarError>;\n\n#[derive(Debug, Snafu, Eq, PartialEq)]\npub enum IsarError {\n    #[snafu(display(\"Isar version of the file is too new or too old to be used.\"))]\n    VersionError {},\n\n    #[snafu(display(\n        \"No such file or directory. Please make sure that the provided path is valid.\"\n    ))]\n    PathError {},\n\n    #[snafu(display(\"Cannot open Environment: {}\", error))]\n    EnvError { error: Box<IsarError> },\n\n    #[snafu(display(\"The database is full.\"))]\n    DbFull {},\n\n    #[snafu(display(\"Unique index violated.\"))]\n    UniqueViolated {},\n\n    #[snafu(display(\"Write transaction required.\"))]\n    WriteTxnRequired {},\n\n    #[snafu(display(\"Auto increment id cannot be generated because the limit is reached.\"))]\n    AutoIncrementOverflow {},\n\n    #[snafu(display(\"The provided ObjectId does not match the collection.\"))]\n    InvalidObjectId {},\n\n    #[snafu(display(\"The provided object is invalid.\"))]\n    InvalidObject {},\n\n    #[snafu(display(\"Transaction closed.\"))]\n    TransactionClosed {},\n\n    #[snafu(display(\"IllegalArg: {}.\", message))]\n    IllegalArg { message: String },\n\n    #[snafu(display(\"Index could not be found.\"))]\n    UnknownIndex {},\n\n    #[snafu(display(\"Invalid JSON.\"))]\n    InvalidJson {},\n\n    #[snafu(display(\"DbCorrupted: {}\", message))]\n    DbCorrupted { message: String },\n\n    #[snafu(display(\"SchemaError: {}\", message))]\n    SchemaError { message: String },\n\n    #[snafu(display(\"SchemaMismatch: The schema of the existing instance does not match.\"))]\n    SchemaMismatch {},\n\n    #[snafu(display(\"InstanceMismatch: The transaction is from a different instance.\"))]\n    InstanceMismatch {},\n\n    #[snafu(display(\"MdbxError ({}): {}\", code, message))]\n    MdbxError { code: i32, message: String },\n}\n\npub fn illegal_arg<T>(msg: &str) -> Result<T> {\n    Err(IsarError::IllegalArg {\n        message: msg.to_string(),\n    })\n}\n\npub fn schema_error<T>(msg: &str) -> Result<T> {\n    Err(IsarError::SchemaError {\n        message: msg.to_string(),\n    })\n}\n"
  },
  {
    "path": "packages/isar_core/src/index/index_key.rs",
    "content": "use crate::index::IsarIndex;\nuse crate::mdbx::Key;\nuse std::borrow::Cow;\nuse std::cmp;\nuse std::cmp::Ordering;\nuse xxhash_rust::xxh3::xxh3_64;\n\n#[derive(Clone, Eq, PartialEq)]\npub struct IndexKey {\n    bytes: Vec<u8>,\n}\n\nimpl IndexKey {\n    pub fn new() -> Self {\n        IndexKey { bytes: vec![] }\n    }\n\n    pub fn from_bytes(bytes: Vec<u8>) -> Self {\n        IndexKey { bytes }\n    }\n\n    pub fn add_byte(&mut self, value: u8) {\n        self.bytes.push(value);\n    }\n\n    pub fn add_int(&mut self, value: i32) {\n        let unsigned = value as u32;\n        let bytes: [u8; 4] = (unsigned ^ 1 << 31).to_be_bytes();\n        self.bytes.extend_from_slice(&bytes);\n    }\n\n    pub fn add_long(&mut self, value: i64) {\n        let unsigned = value as u64;\n        let bytes = (unsigned ^ 1 << 63).to_be_bytes().to_vec();\n        self.bytes.extend_from_slice(&bytes);\n    }\n\n    pub fn add_float(&mut self, value: f32) {\n        let bytes: [u8; 4] = if !value.is_nan() {\n            let bits = if value.is_sign_positive() {\n                value.to_bits() + 2u32.pow(31)\n            } else {\n                !(-value).to_bits() - 2u32.pow(31)\n            };\n            bits.to_be_bytes()\n        } else {\n            [0; 4]\n        };\n        self.bytes.extend_from_slice(&bytes);\n    }\n\n    pub fn add_double(&mut self, value: f64) {\n        let bytes: [u8; 8] = if !value.is_nan() {\n            let bits = if value.is_sign_positive() {\n                value.to_bits() + 2u64.pow(63)\n            } else {\n                !(-value).to_bits() - 2u64.pow(63)\n            };\n            bits.to_be_bytes()\n        } else {\n            [0; 8]\n        };\n        self.bytes.extend_from_slice(&bytes);\n    }\n\n    pub fn add_string(&mut self, value: Option<&str>, case_sensitive: bool) {\n        if let Some(value) = value {\n            let value = if case_sensitive {\n                value.to_string()\n            } else {\n                value.to_lowercase()\n            };\n            let bytes = value.as_bytes();\n            if bytes.len() >= IsarIndex::MAX_STRING_INDEX_SIZE {\n                let index_bytes = &bytes[0..IsarIndex::MAX_STRING_INDEX_SIZE];\n                self.bytes.extend_from_slice(index_bytes);\n                let hash = xxh3_64(bytes);\n                self.bytes.extend_from_slice(&u64::to_le_bytes(hash));\n            } else if bytes.is_empty() {\n                self.bytes.push(1);\n            } else {\n                self.bytes.extend_from_slice(bytes);\n            }\n        } else {\n            self.bytes.push(0);\n        }\n    }\n\n    pub fn add_hash(&mut self, value: u64) {\n        let bytes: [u8; 8] = value.to_be_bytes();\n        self.bytes.extend_from_slice(&bytes);\n    }\n\n    #[allow(clippy::len_without_is_empty)]\n    pub fn len(&self) -> usize {\n        self.bytes.len()\n    }\n\n    pub fn truncate(&mut self, len: usize) {\n        self.bytes.truncate(len);\n    }\n\n    pub fn increase(&mut self) -> bool {\n        let mut increased = false;\n        for i in (0..self.bytes.len()).rev() {\n            if let Some(added) = self.bytes[i].checked_add(1) {\n                self.bytes[i] = added;\n                increased = true;\n                for i2 in (i + 1)..self.bytes.len() {\n                    self.bytes[i2] = 0;\n                }\n                break;\n            }\n        }\n        increased\n    }\n\n    pub fn decrease(&mut self) -> bool {\n        let mut decreased = false;\n        for i in (0..self.bytes.len()).rev() {\n            if let Some(subtracted) = self.bytes[i].checked_sub(1) {\n                self.bytes[i] = subtracted;\n                decreased = true;\n                for i2 in (i + 1)..self.bytes.len() {\n                    self.bytes[i2] = 255;\n                }\n                break;\n            }\n        }\n        decreased\n    }\n}\n\nimpl Key for IndexKey {\n    fn as_bytes(&self) -> Cow<[u8]> {\n        Cow::Borrowed(&self.bytes)\n    }\n\n    fn cmp_bytes(&self, other: &[u8]) -> Ordering {\n        let len = cmp::min(self.bytes.len(), other.len());\n        let cmp = (&self.bytes[0..len]).cmp(&other[0..len]);\n        if cmp == Ordering::Equal {\n            self.bytes.len().cmp(&other.len())\n        } else {\n            cmp\n        }\n    }\n}\n\nimpl PartialOrd<Self> for IndexKey {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for IndexKey {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.cmp_bytes(&other.bytes)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::object::isar_object::IsarObject;\n\n    use super::*;\n    use float_next_after::NextAfter;\n\n    #[test]\n    fn test_add_byte() {\n        let pairs = vec![\n            (IsarObject::NULL_BYTE, vec![123, 0]),\n            (123, vec![123, 123]),\n            (255, vec![123, 255]),\n        ];\n\n        for (val, bytes) in pairs {\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_byte(val);\n            assert_eq!(&index_key.bytes, &bytes);\n        }\n    }\n\n    #[test]\n    fn test_add_int() {\n        let pairs = vec![\n            (i32::MIN, vec![123, 0, 0, 0, 0]),\n            (i32::MIN + 1, vec![123, 0, 0, 0, 1]),\n            (-1, vec![123, 127, 255, 255, 255]),\n            (0, vec![123, 128, 0, 0, 0]),\n            (1, vec![123, 128, 0, 0, 1]),\n            (i32::MAX - 1, vec![123, 255, 255, 255, 254]),\n            (i32::MAX, vec![123, 255, 255, 255, 255]),\n        ];\n\n        for (val, bytes) in pairs {\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_int(val);\n            assert_eq!(&index_key.bytes, &bytes);\n        }\n    }\n\n    #[test]\n    fn test_add_long() {\n        let pairs = vec![\n            (i64::MIN, vec![123, 0, 0, 0, 0, 0, 0, 0, 0]),\n            (i64::MIN + 1, vec![123, 0, 0, 0, 0, 0, 0, 0, 1]),\n            (-1, vec![123, 127, 255, 255, 255, 255, 255, 255, 255]),\n            (0, vec![123, 128, 0, 0, 0, 0, 0, 0, 0]),\n            (1, vec![123, 128, 0, 0, 0, 0, 0, 0, 1]),\n            (\n                i64::MAX - 1,\n                vec![123, 255, 255, 255, 255, 255, 255, 255, 254],\n            ),\n            (i64::MAX, vec![123, 255, 255, 255, 255, 255, 255, 255, 255]),\n        ];\n\n        for (val, bytes) in pairs {\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_long(val);\n            assert_eq!(&index_key.bytes, &bytes);\n        }\n    }\n\n    #[test]\n    fn test_add_float() {\n        let pairs = vec![\n            (f32::NAN, vec![123, 0, 0, 0, 0]),\n            (f32::NEG_INFINITY, vec![123, 0, 127, 255, 255]),\n            (f32::MIN, vec![123, 0, 128, 0, 0]),\n            (f32::MIN.next_after(f32::MAX), vec![123, 0, 128, 0, 1]),\n            ((-0.0).next_after(f32::MIN), vec![123, 127, 255, 255, 254]),\n            (-0.0, vec![123, 127, 255, 255, 255]),\n            (0.0, vec![123, 128, 0, 0, 0]),\n            (0.0.next_after(f32::MAX), vec![123, 128, 0, 0, 1]),\n            (f32::MAX.next_after(f32::MIN), vec![123, 255, 127, 255, 254]),\n            (f32::MAX, vec![123, 255, 127, 255, 255]),\n            (f32::INFINITY, vec![123, 255, 128, 0, 0]),\n        ];\n\n        for (val, bytes) in pairs {\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_float(val);\n            assert_eq!(&index_key.bytes, &bytes);\n        }\n    }\n\n    #[test]\n    fn test_add_double() {\n        let pairs = vec![\n            (f64::NAN, vec![123, 0, 0, 0, 0, 0, 0, 0, 0]),\n            (\n                f64::NEG_INFINITY,\n                vec![123, 0, 15, 255, 255, 255, 255, 255, 255],\n            ),\n            (f64::MIN, vec![123, 0, 16, 0, 0, 0, 0, 0, 0]),\n            (\n                f64::MIN.next_after(f64::MAX),\n                vec![123, 0, 16, 0, 0, 0, 0, 0, 1],\n            ),\n            (\n                (-0.0).next_after(f64::MIN),\n                vec![123, 127, 255, 255, 255, 255, 255, 255, 254],\n            ),\n            (-0.0, vec![123, 127, 255, 255, 255, 255, 255, 255, 255]),\n            (0.0, vec![123, 128, 0, 0, 0, 0, 0, 0, 0]),\n            (\n                0.0.next_after(f64::MAX),\n                vec![123, 128, 0, 0, 0, 0, 0, 0, 1],\n            ),\n            (\n                f64::MAX.next_after(f64::MIN),\n                vec![123, 255, 239, 255, 255, 255, 255, 255, 254],\n            ),\n            (f64::MAX, vec![123, 255, 239, 255, 255, 255, 255, 255, 255]),\n            (f64::INFINITY, vec![123, 255, 240, 0, 0, 0, 0, 0, 0]),\n        ];\n\n        for (val, bytes) in pairs {\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_double(val);\n            assert_eq!(&index_key.bytes, &bytes);\n        }\n    }\n\n    #[test]\n    fn test_add_string() {\n        let long_str = (0..850).map(|_| \"aB\").collect::<String>();\n        let long_str_lc = long_str.to_lowercase();\n\n        let mut long_str_bytes = vec![123];\n        long_str_bytes.extend_from_slice(long_str.as_bytes());\n\n        let mut long_str_lc_bytes = vec![123];\n        long_str_lc_bytes.extend_from_slice(long_str_lc.as_bytes());\n\n        let mut hello_bytes = vec![123];\n        hello_bytes.extend_from_slice(b\"hELLO\");\n\n        let mut hello_bytes_lc = vec![123];\n        hello_bytes_lc.extend_from_slice(b\"hello\");\n\n        let pairs: Vec<(Option<&str>, Vec<u8>, Vec<u8>)> = vec![\n            (None, vec![123, 0], vec![123, 0]),\n            (Some(\"\"), vec![123, 1], vec![123, 1]),\n            (\n                Some(\"hello\"),\n                hello_bytes_lc.clone(),\n                hello_bytes_lc.clone(),\n            ),\n            (Some(\"hELLO\"), hello_bytes.clone(), hello_bytes_lc.clone()),\n            //(Some(&long_str), long_str_bytes, long_str_lc_bytes),\n        ];\n\n        for (str, bytes, bytes_lc) in pairs {\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_string(str, true);\n            assert_eq!(index_key.bytes, bytes);\n\n            let mut index_key = IndexKey::new();\n            index_key.add_byte(123);\n            index_key.add_string(str, false);\n            assert_eq!(index_key.bytes, bytes_lc);\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/index/index_key_builder.rs",
    "content": "use crate::error::Result;\nuse crate::index::index_key::IndexKey;\nuse crate::index::IndexProperty;\nuse crate::object::data_type::DataType;\nuse crate::object::isar_object::IsarObject;\nuse crate::schema::index_schema::IndexType;\n\npub(crate) struct IndexKeyBuilder<'a> {\n    properties: &'a [IndexProperty],\n}\n\nimpl<'a> IndexKeyBuilder<'a> {\n    pub fn new(properties: &'a [IndexProperty]) -> Self {\n        Self { properties }\n    }\n\n    pub fn create_keys(\n        &self,\n        object: IsarObject,\n        mut callback: impl FnMut(&IndexKey) -> Result<bool>,\n    ) -> Result<bool> {\n        let first = self.properties.first().unwrap();\n        if !first.is_multi_entry() {\n            let key = self.create_primitive_key(object);\n            callback(&key)?;\n            Ok(true)\n        } else {\n            assert_eq!(self.properties.len(), 1);\n            Self::create_list_keys(first, object, &mut callback)\n        }\n    }\n\n    pub fn create_primitive_key(&self, object: IsarObject) -> IndexKey {\n        let mut key = IndexKey::new();\n        for index_property in self.properties {\n            let property = &index_property.property;\n\n            if index_property.index_type == IndexType::Hash {\n                let hash = object.hash_property(\n                    property.offset,\n                    property.data_type,\n                    index_property.case_sensitive,\n                    0,\n                );\n                key.add_hash(hash);\n            } else {\n                match property.data_type {\n                    DataType::Bool | DataType::Byte => {\n                        assert_eq!(IsarObject::NULL_BOOL, IsarObject::NULL_BYTE);\n                        key.add_byte(object.read_byte(property.offset))\n                    }\n                    DataType::Int => key.add_int(object.read_int(property.offset)),\n                    DataType::Float => key.add_float(object.read_float(property.offset)),\n                    DataType::Long => key.add_long(object.read_long(property.offset)),\n                    DataType::Double => key.add_double(object.read_double(property.offset)),\n                    DataType::String => key.add_string(\n                        object.read_string(property.offset),\n                        index_property.case_sensitive,\n                    ),\n                    _ => unreachable!(),\n                }\n            }\n        }\n        key\n    }\n\n    fn create_list_keys(\n        index_property: &IndexProperty,\n        object: IsarObject,\n        mut callback: impl FnMut(&IndexKey) -> Result<bool>,\n    ) -> Result<bool> {\n        let mut key = IndexKey::new();\n        let property = &index_property.property;\n        if object.is_null(property.offset, property.data_type) {\n            return Ok(true);\n        }\n        match property.data_type {\n            DataType::BoolList | DataType::ByteList => {\n                for value in object.read_byte_list(property.offset).unwrap() {\n                    key.truncate(0);\n                    key.add_byte(*value);\n                    if !callback(&key)? {\n                        return Ok(false);\n                    }\n                }\n            }\n            DataType::IntList => {\n                for value in object.read_int_list(property.offset).unwrap() {\n                    key.truncate(0);\n                    key.add_int(value);\n                    if !callback(&key)? {\n                        return Ok(false);\n                    }\n                }\n            }\n            DataType::LongList => {\n                for value in object.read_long_list(property.offset).unwrap() {\n                    key.truncate(0);\n                    key.add_long(value);\n                    if !callback(&key)? {\n                        return Ok(false);\n                    }\n                }\n            }\n            DataType::FloatList => {\n                for value in object.read_float_list(property.offset).unwrap() {\n                    key.truncate(0);\n                    key.add_float(value);\n                    if !callback(&key)? {\n                        return Ok(false);\n                    }\n                }\n            }\n            DataType::DoubleList => {\n                for value in object.read_double_list(property.offset).unwrap() {\n                    key.truncate(0);\n                    key.add_double(value);\n                    if !callback(&key)? {\n                        return Ok(false);\n                    }\n                }\n            }\n            DataType::StringList => {\n                for value in object.read_string_list(property.offset).unwrap() {\n                    key.truncate(0);\n                    if index_property.index_type == IndexType::HashElements {\n                        let hash = IsarObject::hash_string(value, index_property.case_sensitive, 0);\n                        key.add_hash(hash);\n                    } else {\n                        key.add_string(value, index_property.case_sensitive);\n                    }\n                    if !callback(&key)? {\n                        return Ok(false);\n                    }\n                }\n            }\n            _ => unreachable!(),\n        }\n        Ok(true)\n    }\n}\n\n#[cfg(test)]\nmod tests {}\n"
  },
  {
    "path": "packages/isar_core/src/index/mod.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::{IsarError, Result};\nuse crate::index::index_key::IndexKey;\nuse crate::index::index_key_builder::IndexKeyBuilder;\nuse crate::mdbx::db::Db;\nuse crate::object::id::{BytesToId, IdToBytes};\nuse crate::object::isar_object::IsarObject;\nuse crate::object::property::Property;\nuse crate::schema::index_schema::IndexType;\nuse intmap::IntMap;\nuse xxhash_rust::xxh3::xxh3_64;\n\npub mod index_key;\npub(crate) mod index_key_builder;\n\n#[derive(Clone, Eq, PartialEq)]\npub struct IndexProperty {\n    pub property: Property,\n    pub index_type: IndexType,\n    pub case_sensitive: bool,\n}\n\nimpl IndexProperty {\n    pub(crate) fn new(property: Property, index_type: IndexType, case_sensitive: bool) -> Self {\n        IndexProperty {\n            property,\n            index_type,\n            case_sensitive,\n        }\n    }\n\n    pub fn get_string_with_case(&self, object: IsarObject) -> Option<String> {\n        object.read_string(self.property.offset).map(|str| {\n            if self.case_sensitive {\n                str.to_string()\n            } else {\n                str.to_lowercase()\n            }\n        })\n    }\n\n    fn is_multi_entry(&self) -> bool {\n        self.property.data_type.get_element_type().is_some() && self.index_type != IndexType::Hash\n    }\n}\n\n#[derive(Clone, Eq, PartialEq)]\npub(crate) struct IsarIndex {\n    pub name: String,\n    pub id: u64,\n    pub properties: Vec<IndexProperty>,\n    pub unique: bool,\n    pub replace: bool,\n    pub multi_entry: bool,\n    db: Db,\n}\n\nimpl IsarIndex {\n    pub const MAX_STRING_INDEX_SIZE: usize = 1024;\n\n    pub fn new(\n        name: &str,\n        db: Db,\n        properties: Vec<IndexProperty>,\n        unique: bool,\n        replace: bool,\n    ) -> Self {\n        let id = xxh3_64(name.as_bytes());\n        let multi_entry = properties.first().unwrap().is_multi_entry();\n        IsarIndex {\n            name: name.to_string(),\n            id,\n            properties,\n            unique,\n            replace,\n            multi_entry,\n            db,\n        }\n    }\n\n    pub fn create_for_object<F>(\n        &self,\n        cursors: &IsarCursors,\n        id: i64,\n        object: IsarObject,\n        mut delete: F,\n    ) -> Result<()>\n    where\n        F: FnMut(i64) -> Result<()>,\n    {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        let key_builder = IndexKeyBuilder::new(&self.properties);\n        key_builder.create_keys(object, |key| {\n            if self.unique {\n                let existing = cursor.move_to(key)?;\n                if let Some((_, existing_id_bytes)) = existing {\n                    let existing_id = existing_id_bytes.to_id();\n                    if self.replace && existing_id != id {\n                        delete(existing_id)?;\n                    } else {\n                        return Err(IsarError::UniqueViolated {});\n                    }\n                }\n            }\n            cursor.put(key, &id.to_id_bytes())?;\n            Ok(true)\n        })?;\n\n        Ok(())\n    }\n\n    pub fn delete_for_object(\n        &self,\n        cursors: &IsarCursors,\n        id: i64,\n        object: IsarObject,\n    ) -> Result<()> {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        let key_builder = IndexKeyBuilder::new(&self.properties);\n        key_builder.create_keys(object, |key| {\n            let entry = if self.unique {\n                cursor.move_to(key)?\n            } else {\n                cursor.move_to_key_val(key, &id.to_id_bytes())?\n            };\n            if entry.is_some() {\n                cursor.delete_current()?;\n            }\n            Ok(true)\n        })?;\n        Ok(())\n    }\n\n    pub fn iter_between<'txn, 'env>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        lower_key: &IndexKey,\n        upper_key: &IndexKey,\n        skip_duplicates: bool,\n        ascending: bool,\n        mut callback: impl FnMut(i64) -> Result<bool>,\n    ) -> Result<bool> {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        cursor.iter_between(\n            lower_key,\n            upper_key,\n            !self.unique,\n            skip_duplicates,\n            ascending,\n            |_, _, id_bytes| callback(id_bytes.to_id()),\n        )\n    }\n\n    pub fn get_id<'txn, 'env>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        key: &IndexKey,\n    ) -> Result<Option<i64>> {\n        let mut result = None;\n        self.iter_between(cursors, key, key, false, true, |id| {\n            result = Some(id);\n            Ok(false)\n        })?;\n        Ok(result)\n    }\n\n    pub fn get_size(&self, cursors: &IsarCursors) -> Result<u64> {\n        Ok(cursors.db_stat(self.db)?.1)\n    }\n\n    pub fn clear(&self, cursors: &IsarCursors) -> Result<()> {\n        cursors.clear_db(self.db)\n    }\n\n    pub fn verify(&self, cursors: &IsarCursors, objects: &IntMap<IsarObject>) -> Result<()> {\n        let mut count = 0;\n\n        let mut cursor = cursors.get_cursor(self.db)?;\n        for id in objects.keys() {\n            let id = *id;\n            let object = *objects.get(id).unwrap();\n            let key_builder = IndexKeyBuilder::new(&self.properties);\n            key_builder.create_keys(object, |key| {\n                count += 1;\n\n                let result = cursor.move_to_key_val(key, &(id as i64).to_id_bytes())?;\n                if result.is_some() {\n                    Ok(true)\n                } else {\n                    Err(IsarError::DbCorrupted {\n                        message: \"Missing index entry.\".to_string(),\n                    })\n                }\n            })?;\n        }\n\n        if cursors.db_stat(self.db)?.0 != count {\n            Err(IsarError::DbCorrupted {\n                message: \"Obsolete index entry.\".to_string(),\n            })\n        } else {\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/instance.rs",
    "content": "use crate::collection::IsarCollection;\nuse crate::error::*;\nuse crate::mdbx::env::Env;\nuse crate::query::Query;\nuse crate::schema::schema_manager::SchemaManager;\nuse crate::schema::Schema;\nuse crate::txn::IsarTxn;\nuse crate::watch::change_set::ChangeSet;\nuse crate::watch::isar_watchers::{IsarWatchers, WatcherModifier};\nuse crate::watch::watcher::WatcherCallback;\nuse crate::watch::WatchHandle;\nuse crossbeam_channel::{unbounded, Sender};\nuse intmap::IntMap;\nuse once_cell::sync::Lazy;\nuse std::fs::remove_file;\nuse std::fs::{self, metadata};\nuse std::path::PathBuf;\nuse std::sync::atomic::{AtomicU64, Ordering};\nuse std::sync::{Arc, Mutex, RwLock};\nuse xxhash_rust::xxh3::xxh3_64;\n\nstatic INSTANCES: Lazy<RwLock<IntMap<Arc<IsarInstance>>>> =\n    Lazy::new(|| RwLock::new(IntMap::new()));\n\nstatic WATCHER_ID: AtomicU64 = AtomicU64::new(0);\n\npub struct CompactCondition {\n    pub min_file_size: u64,\n    pub min_bytes: u64,\n    pub min_ratio: f64,\n}\n\npub struct IsarInstance {\n    pub name: String,\n    pub dir: String,\n    pub collections: Vec<IsarCollection>,\n    pub(crate) instance_id: u64,\n\n    env: Env,\n    watchers: Mutex<IsarWatchers>,\n    watcher_modifier_sender: Sender<WatcherModifier>,\n}\n\nimpl IsarInstance {\n    pub fn open(\n        name: &str,\n        dir: Option<&str>,\n        schema: Schema,\n        max_size_mib: usize,\n        relaxed_durability: bool,\n        compact_condition: Option<CompactCondition>,\n    ) -> Result<Arc<Self>> {\n        let mut lock = INSTANCES.write().unwrap();\n        let instance_id = xxh3_64(name.as_bytes());\n        if let Some(instance) = lock.get(instance_id) {\n            Ok(instance.clone())\n        } else {\n            if let Some(dir) = dir {\n                let new_instance = Self::open_internal(\n                    name,\n                    dir,\n                    instance_id,\n                    schema,\n                    max_size_mib,\n                    relaxed_durability,\n                    compact_condition,\n                )?;\n                let new_instance = Arc::new(new_instance);\n                lock.insert(instance_id, new_instance.clone());\n                Ok(new_instance)\n            } else {\n                Err(IsarError::IllegalArg {\n                    message: \"Please provide a valid directory.\".to_string(),\n                })\n            }\n        }\n    }\n\n    fn get_isar_path(name: &str, dir: &str) -> String {\n        let mut file_name = name.to_string();\n        file_name.push_str(\".isar\");\n\n        let mut path_buf = PathBuf::from(dir);\n        path_buf.push(file_name);\n        path_buf.as_path().to_str().unwrap().to_string()\n    }\n\n    fn move_old_database(name: &str, dir: &str, new_path: &str) {\n        let mut old_path_buf = PathBuf::from(dir);\n        old_path_buf.push(name);\n        old_path_buf.push(\"mdbx.dat\");\n        let old_path = old_path_buf.as_path();\n\n        let result = fs::rename(old_path, new_path);\n\n        // Also try to migrate the previous default isar name\n        if name == \"default\" && result.is_err() {\n            Self::move_old_database(\"isar\", dir, new_path)\n        }\n    }\n\n    fn open_internal(\n        name: &str,\n        dir: &str,\n        instance_id: u64,\n        mut schema: Schema,\n        max_size_mib: usize,\n        relaxed_durability: bool,\n        compact_condition: Option<CompactCondition>,\n    ) -> Result<Self> {\n        let isar_file = Self::get_isar_path(name, dir);\n\n        Self::move_old_database(name, dir, &isar_file);\n\n        let db_count = schema.count_dbs() as u64 + 3;\n        let env = Env::create(\n            &isar_file,\n            db_count,\n            max_size_mib.max(1),\n            relaxed_durability,\n        )\n        .map_err(|e| IsarError::EnvError { error: Box::new(e) })?;\n\n        let txn = env.txn(true)?;\n        let mut manager = SchemaManager::create(instance_id, &txn)?;\n        txn.commit()?;\n\n        let txn = env.txn(true)?;\n        let added_indexes = manager.migrate_schema(&txn, &mut schema)?;\n        txn.commit()?;\n\n        let mut collections = vec![];\n        for col_schema in &schema.collections {\n            let txn = env.txn(true)?;\n            let col_id = xxh3_64(col_schema.name.as_bytes());\n            let added_indexes = added_indexes\n                .get(col_id)\n                .map(|v| v.as_slice())\n                .unwrap_or_default();\n            let col = manager.open_collection(&txn, col_schema, &schema, added_indexes)?;\n            collections.push(col);\n            txn.commit()?;\n        }\n\n        if !manager.schemas.is_empty() {\n            let txn = env.txn(true)?;\n            manager.delete_unopened_collections(&txn)?;\n            txn.commit()?;\n        }\n\n        let (tx, rx) = unbounded();\n\n        let instance = IsarInstance {\n            env,\n            name: name.to_string(),\n            dir: dir.to_string(),\n            collections,\n            instance_id,\n            watchers: Mutex::new(IsarWatchers::new(rx)),\n            watcher_modifier_sender: tx,\n        };\n\n        if let Some(compact_condition) = compact_condition {\n            let instance = instance.compact(compact_condition)?;\n            if let Some(instance) = instance {\n                Ok(instance)\n            } else {\n                Self::open_internal(\n                    name,\n                    dir,\n                    instance_id,\n                    schema,\n                    max_size_mib,\n                    relaxed_durability,\n                    None,\n                )\n            }\n        } else {\n            Ok(instance)\n        }\n    }\n\n    fn compact(self, compact_condition: CompactCondition) -> Result<Option<Self>> {\n        let mut txn = self.begin_txn(false, true)?;\n        let instance_size = self.get_size(&mut txn, true, true)?;\n        txn.abort();\n\n        let isar_file = Self::get_isar_path(&self.name, &self.dir);\n        let file_size = metadata(&isar_file)\n            .map_err(|_| IsarError::PathError {})?\n            .len();\n\n        let compact_bytes = file_size.saturating_sub(instance_size);\n        let compact_ratio = if instance_size == 0 {\n            f64::INFINITY\n        } else {\n            (file_size as f64) / (instance_size as f64)\n        };\n        let should_compact = file_size >= compact_condition.min_file_size\n            && compact_bytes >= compact_condition.min_bytes\n            && compact_ratio >= compact_condition.min_ratio;\n\n        if should_compact {\n            let compact_file = format!(\"{}.compact\", &isar_file);\n            self.copy_to_file(&compact_file)?;\n            drop(self);\n\n            let _ = fs::rename(&compact_file, &isar_file);\n            Ok(None)\n        } else {\n            Ok(Some(self))\n        }\n    }\n\n    pub fn get_instance(name: &str) -> Option<Arc<Self>> {\n        let instance_id = xxh3_64(name.as_bytes());\n        INSTANCES.read().unwrap().get(instance_id).cloned()\n    }\n\n    pub fn begin_txn(&self, write: bool, silent: bool) -> Result<IsarTxn> {\n        let change_set = if write && !silent {\n            let mut watchers_lock = self.watchers.lock().unwrap();\n            watchers_lock.sync();\n            let change_set = ChangeSet::new(watchers_lock);\n            Some(change_set)\n        } else {\n            None\n        };\n\n        let txn = self.env.txn(write)?;\n        IsarTxn::new(self.instance_id, txn, write, change_set)\n    }\n\n    pub fn get_size(\n        &self,\n        txn: &mut IsarTxn,\n        include_indexes: bool,\n        include_links: bool,\n    ) -> Result<u64> {\n        let mut size = 0;\n\n        for col in &self.collections {\n            size += col.get_size(txn, include_indexes, include_links)?;\n        }\n\n        Ok(size)\n    }\n\n    pub fn copy_to_file(&self, path: &str) -> Result<()> {\n        self.env.copy(path)\n    }\n\n    fn new_watcher(&self, start: WatcherModifier, stop: WatcherModifier) -> WatchHandle {\n        self.watcher_modifier_sender.try_send(start).unwrap();\n\n        let sender = self.watcher_modifier_sender.clone();\n        WatchHandle::new(Box::new(move || {\n            let _ = sender.try_send(stop);\n        }))\n    }\n\n    pub fn watch_collection(\n        &self,\n        collection: &IsarCollection,\n        callback: WatcherCallback,\n    ) -> WatchHandle {\n        let watcher_id = WATCHER_ID.fetch_add(1, Ordering::SeqCst);\n        let col_id = collection.id;\n        self.new_watcher(\n            Box::new(move |iw| {\n                iw.get_col_watchers(col_id)\n                    .add_watcher(watcher_id, callback);\n            }),\n            Box::new(move |iw| {\n                iw.get_col_watchers(col_id).remove_watcher(watcher_id);\n            }),\n        )\n    }\n\n    pub fn watch_object(\n        &self,\n        collection: &IsarCollection,\n        oid: i64,\n        callback: WatcherCallback,\n    ) -> WatchHandle {\n        let watcher_id = WATCHER_ID.fetch_add(1, Ordering::SeqCst);\n        let col_id = collection.id;\n        self.new_watcher(\n            Box::new(move |iw| {\n                iw.get_col_watchers(col_id)\n                    .add_object_watcher(watcher_id, oid, callback);\n            }),\n            Box::new(move |iw| {\n                iw.get_col_watchers(col_id)\n                    .remove_object_watcher(oid, watcher_id);\n            }),\n        )\n    }\n\n    pub fn watch_query(\n        &self,\n        collection: &IsarCollection,\n        query: Query,\n        callback: WatcherCallback,\n    ) -> WatchHandle {\n        let watcher_id = WATCHER_ID.fetch_add(1, Ordering::SeqCst);\n        let col_id = collection.id;\n        self.new_watcher(\n            Box::new(move |iw| {\n                iw.get_col_watchers(col_id)\n                    .add_query_watcher(watcher_id, query, callback);\n            }),\n            Box::new(move |iw| {\n                iw.get_col_watchers(col_id).remove_query_watcher(watcher_id);\n            }),\n        )\n    }\n\n    fn close_internal(self: Arc<Self>, delete_from_disk: bool) -> bool {\n        // Check whether all other references are gone\n        if Arc::strong_count(&self) == 2 {\n            let mut lock = INSTANCES.write().unwrap();\n            // Check again to make sure there are no new references\n            if Arc::strong_count(&self) == 2 {\n                lock.remove(self.instance_id);\n\n                if delete_from_disk {\n                    let mut path = Self::get_isar_path(&self.name, &self.dir);\n                    drop(self);\n                    let _ = remove_file(&path);\n                    path.push_str(\".lock\");\n                    let _ = remove_file(&path);\n                }\n                return true;\n            }\n        }\n        false\n    }\n\n    pub fn close(self: Arc<Self>) -> bool {\n        self.close_internal(false)\n    }\n\n    pub fn close_and_delete(self: Arc<Self>) -> bool {\n        self.close_internal(true)\n    }\n\n    pub fn verify(&self, txn: &mut IsarTxn) -> Result<()> {\n        let mut db_names = vec![];\n        db_names.push(\"_info\".to_string());\n        for col in &self.collections {\n            db_names.push(col.name.clone());\n            for index in &col.indexes {\n                db_names.push(format!(\"_i_{}_{}\", col.name, index.name));\n            }\n\n            for link in &col.links {\n                db_names.push(format!(\"_l_{}_{}\", col.name, link.name));\n                db_names.push(format!(\"_b_{}_{}\", col.name, link.name));\n            }\n        }\n        let mut actual_db_names = txn.db_names()?;\n\n        db_names.sort();\n        actual_db_names.sort();\n\n        if db_names != actual_db_names {\n            Err(IsarError::DbCorrupted {\n                message: \"Incorrect databases\".to_string(),\n            })\n        } else {\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/legacy/isar_object_v1.rs",
    "content": "use crate::object::{data_type::DataType, isar_object::IsarObject};\nuse byteorder::{ByteOrder, LittleEndian};\n\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct LegacyProperty {\n    pub data_type: DataType,\n    pub offset: usize,\n}\n\nimpl LegacyProperty {\n    pub const fn new(data_type: DataType, offset: usize) -> Self {\n        LegacyProperty { data_type, offset }\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct LegacyIsarObject<'a> {\n    bytes: &'a [u8],\n    static_size: usize,\n}\n\nimpl<'a> LegacyIsarObject<'a> {\n    pub fn from_bytes(bytes: &'a [u8]) -> Self {\n        let static_size = LittleEndian::read_u16(bytes) as usize;\n        LegacyIsarObject { bytes, static_size }\n    }\n\n    #[inline]\n    pub(crate) fn contains_offset(&self, offset: usize) -> bool {\n        self.static_size > offset\n    }\n\n    #[inline]\n    pub fn contains_property(&self, property: LegacyProperty) -> bool {\n        self.contains_offset(property.offset)\n    }\n\n    pub fn is_null(&self, property: LegacyProperty) -> bool {\n        match property.data_type {\n            DataType::Byte => self.read_byte(property) == IsarObject::NULL_BYTE,\n            DataType::Int => self.read_int(property) == IsarObject::NULL_INT,\n            DataType::Long => self.read_long(property) == IsarObject::NULL_LONG,\n            DataType::Float => self.read_float(property).is_nan(),\n            DataType::Double => self.read_double(property).is_nan(),\n            _ => self.get_offset_length(property.offset, false).is_none(),\n        }\n    }\n\n    pub fn read_byte(&self, property: LegacyProperty) -> u8 {\n        if self.contains_property(property) {\n            self.bytes[property.offset]\n        } else {\n            IsarObject::NULL_BYTE\n        }\n    }\n\n    pub fn read_bool(&self, property: LegacyProperty) -> bool {\n        self.read_byte(property) == IsarObject::TRUE_BOOL\n    }\n\n    pub fn read_int(&self, property: LegacyProperty) -> i32 {\n        if self.contains_property(property) {\n            LittleEndian::read_i32(&self.bytes[property.offset..])\n        } else {\n            IsarObject::NULL_INT\n        }\n    }\n\n    pub fn read_float(&self, property: LegacyProperty) -> f32 {\n        if self.contains_property(property) {\n            LittleEndian::read_f32(&self.bytes[property.offset..])\n        } else {\n            IsarObject::NULL_FLOAT\n        }\n    }\n\n    pub fn read_long(&self, property: LegacyProperty) -> i64 {\n        if self.contains_property(property) {\n            LittleEndian::read_i64(&self.bytes[property.offset..])\n        } else {\n            IsarObject::NULL_LONG\n        }\n    }\n\n    pub fn read_double(&self, property: LegacyProperty) -> f64 {\n        if self.contains_property(property) {\n            LittleEndian::read_f64(&self.bytes[property.offset..])\n        } else {\n            IsarObject::NULL_DOUBLE\n        }\n    }\n\n    fn get_offset_length(&self, offset: usize, dynamic_offset: bool) -> Option<(usize, usize)> {\n        if dynamic_offset || self.contains_offset(offset) {\n            let list_offset = LittleEndian::read_u32(&self.bytes[offset..]) as usize;\n            let length = LittleEndian::read_u32(&self.bytes[offset + 4..]);\n            if list_offset != 0 {\n                return Some((list_offset as usize, length as usize));\n            }\n        }\n        None\n    }\n\n    fn read_string_at(&self, offset: usize, dynamic_offset: bool) -> Option<&'a str> {\n        let (offset, length) = self.get_offset_length(offset, dynamic_offset)?;\n        let str = unsafe { std::str::from_utf8_unchecked(&self.bytes[offset..offset + length]) };\n        Some(str)\n    }\n\n    pub fn read_string(&'a self, property: LegacyProperty) -> Option<&'a str> {\n        self.read_string_at(property.offset, false)\n    }\n\n    pub fn read_byte_list(&self, property: LegacyProperty) -> Option<&'a [u8]> {\n        let (offset, length) = self.get_offset_length(property.offset, false)?;\n        Some(&self.bytes[offset..offset + length])\n    }\n\n    pub fn read_int_list(&self, property: LegacyProperty) -> Option<Vec<i32>> {\n        let (offset, length) = self.get_offset_length(property.offset, false)?;\n        let list = (offset..offset + length * 4)\n            .step_by(4)\n            .into_iter()\n            .map(|offset| LittleEndian::read_i32(&self.bytes[offset..]))\n            .collect();\n        Some(list)\n    }\n\n    pub fn read_float_list(&self, property: LegacyProperty) -> Option<Vec<f32>> {\n        let (offset, length) = self.get_offset_length(property.offset, false)?;\n        let list = (offset..offset + length * 4)\n            .step_by(4)\n            .into_iter()\n            .map(|offset| LittleEndian::read_f32(&self.bytes[offset..]))\n            .collect();\n        Some(list)\n    }\n\n    pub fn read_long_list(&self, property: LegacyProperty) -> Option<Vec<i64>> {\n        let (offset, length) = self.get_offset_length(property.offset, false)?;\n        let list = (offset..offset + length * 8)\n            .step_by(8)\n            .into_iter()\n            .map(|offset| LittleEndian::read_i64(&self.bytes[offset..]))\n            .collect();\n        Some(list)\n    }\n\n    pub fn read_double_list(&self, property: LegacyProperty) -> Option<Vec<f64>> {\n        let (offset, length) = self.get_offset_length(property.offset, false)?;\n        let list = (offset..offset + length * 8)\n            .step_by(8)\n            .into_iter()\n            .map(|offset| LittleEndian::read_f64(&self.bytes[offset..]))\n            .collect();\n        Some(list)\n    }\n\n    pub fn read_string_list(&self, property: LegacyProperty) -> Option<Vec<Option<&'a str>>> {\n        let (offset, length) = self.get_offset_length(property.offset, false)?;\n        let list = (offset..offset + length * 8)\n            .step_by(8)\n            .into_iter()\n            .map(|offset| self.read_string_at(offset, true))\n            .collect();\n        Some(list)\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/legacy/mod.rs",
    "content": "pub(crate) mod isar_object_v1;\n"
  },
  {
    "path": "packages/isar_core/src/lib.rs",
    "content": "#![allow(clippy::new_without_default)]\n\n#[cfg(not(target_endian = \"little\"))]\ncompile_error!(\"Only little endian systems are supported.\");\n\npub mod collection;\nmod cursor;\npub mod error;\npub mod index;\npub mod instance;\nmod legacy;\nmod link;\nmod mdbx;\npub mod object;\npub mod query;\npub mod schema;\npub mod txn;\npub mod watch;\n"
  },
  {
    "path": "packages/isar_core/src/link.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::{IsarError, Result};\nuse crate::mdbx::cursor::Cursor;\nuse crate::mdbx::db::Db;\nuse crate::object::id::{BytesToId, IdToBytes};\nuse crate::object::isar_object::IsarObject;\nuse std::ops::Deref;\nuse xxhash_rust::xxh3::xxh3_64_with_seed;\n\n#[derive(Clone)]\npub(crate) struct IsarLink {\n    pub name: String,\n    pub id: u64,\n    db: Db,\n    bl_db: Db,\n    source_db: Db,\n    target_db: Db,\n}\n\nimpl IsarLink {\n    pub fn new(\n        collection: &str,\n        name: &str,\n        backlink: bool,\n        db: Db,\n        bl_db: Db,\n        source_db: Db,\n        target_db: Db,\n    ) -> IsarLink {\n        let seed = if backlink { 1 } else { 0 };\n        let seed = xxh3_64_with_seed(collection.as_bytes(), seed);\n        let id = xxh3_64_with_seed(name.as_bytes(), seed);\n        IsarLink {\n            name: name.to_string(),\n            id,\n            db,\n            bl_db,\n            source_db,\n            target_db,\n        }\n    }\n\n    pub fn iter_ids<F>(&self, cursors: &IsarCursors, id: i64, mut callback: F) -> Result<bool>\n    where\n        F: FnMut(&mut Cursor, i64) -> Result<bool>,\n    {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        cursor.iter_dups(&id, |cursor, link_target_key| {\n            callback(cursor, link_target_key.to_id())\n        })\n    }\n\n    pub fn iter<'txn, 'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        id: i64,\n        mut callback: F,\n    ) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        let mut target_cursor = cursors.get_cursor(self.target_db)?;\n        self.iter_ids(cursors, id, |_, link_target_key| {\n            if let Some((id_bytes, object)) = target_cursor.move_to(&link_target_key)? {\n                callback(id_bytes.deref().to_id(), IsarObject::from_bytes(&object))\n            } else {\n                Err(IsarError::DbCorrupted {\n                    message: \"Target object does not exist\".to_string(),\n                })\n            }\n        })\n    }\n\n    pub fn create(&self, cursors: &IsarCursors, source_id: i64, target_id: i64) -> Result<bool> {\n        let mut source_cursor = cursors.get_cursor(self.source_db)?;\n        let mut target_cursor = cursors.get_cursor(self.target_db)?;\n\n        let exists_source = source_cursor.move_to(&source_id)?.is_some();\n        let exists_target = target_cursor.move_to(&target_id)?.is_some();\n        if !exists_source || !exists_target {\n            return Ok(false);\n        }\n\n        let mut link_cursor = cursors.get_cursor(self.db)?;\n        link_cursor.put(&source_id, &target_id.to_id_bytes())?;\n\n        let mut backlink_cursor = cursors.get_cursor(self.bl_db)?;\n        backlink_cursor.put(&target_id, &source_id.to_id_bytes())?;\n        Ok(true)\n    }\n\n    pub fn delete(&self, cursors: &IsarCursors, source_id: i64, target_id: i64) -> Result<bool> {\n        let mut link_cursor = cursors.get_cursor(self.db)?;\n        let exists = link_cursor\n            .move_to_key_val(&source_id, &target_id.to_id_bytes())?\n            .is_some();\n\n        if exists {\n            let mut backlink_cursor = cursors.get_cursor(self.bl_db)?;\n            let backlink_exists = backlink_cursor\n                .move_to_key_val(&target_id, &source_id.to_id_bytes())?\n                .is_some();\n            if backlink_exists {\n                link_cursor.delete_current()?;\n                backlink_cursor.delete_current()?;\n                Ok(true)\n            } else {\n                Err(IsarError::DbCorrupted {\n                    message: \"Backlink does not exist\".to_string(),\n                })\n            }\n        } else {\n            Ok(false)\n        }\n    }\n\n    pub fn delete_all_for_object(&self, cursors: &IsarCursors, id: i64) -> Result<()> {\n        let id_bytes = id.to_id_bytes();\n\n        let mut backlink_cursor = cursors.get_cursor(self.bl_db)?;\n        self.iter_ids(cursors, id, |cursor, link_target_key| {\n            let exists = backlink_cursor\n                .move_to_key_val(&link_target_key, &id_bytes)?\n                .is_some();\n            if exists {\n                cursor.delete_current()?;\n                backlink_cursor.delete_current()?;\n                Ok(true)\n            } else {\n                Err(IsarError::DbCorrupted {\n                    message: \"Backlink does not exist\".to_string(),\n                })\n            }\n        })?;\n        Ok(())\n    }\n\n    pub fn get_size(&self, cursors: &IsarCursors) -> Result<u64> {\n        Ok(cursors.db_stat(self.db)?.1)\n    }\n\n    pub fn clear(&self, cursors: &IsarCursors) -> Result<()> {\n        cursors.clear_db(self.db)?;\n        cursors.clear_db(self.bl_db)\n    }\n\n    pub fn verify(&self, cursors: &IsarCursors, links: &[(i64, i64)]) -> Result<()> {\n        let link_count = cursors.db_stat(self.db)?.0 as usize;\n        let backlink_count = cursors.db_stat(self.db)?.0 as usize;\n        if link_count != links.len() || backlink_count != links.len() {\n            return Err(IsarError::DbCorrupted {\n                message: \"Link or Backlink count mismatch.\".to_string(),\n            });\n        }\n\n        let mut cursor = cursors.get_cursor(self.db)?;\n        cursor.iter_all(false, true, |_, id_bytes, target_id_bytes| {\n            let id = id_bytes.to_id();\n            let target_id = target_id_bytes.to_id();\n            if links.contains(&(id, target_id)) {\n                Ok(true)\n            } else {\n                Err(IsarError::DbCorrupted {\n                    message: \"Unknown link in database.\".to_string(),\n                })\n            }\n        })?;\n\n        let mut cursor = cursors.get_cursor(self.bl_db)?;\n        cursor.iter_all(false, true, |_, target_id_bytes, id_bytes| {\n            let id = id_bytes.to_id();\n            let target_id = target_id_bytes.to_id();\n            if links.contains(&(id, target_id)) {\n                Ok(true)\n            } else {\n                Err(IsarError::DbCorrupted {\n                    message: \"Unknown link in database.\".to_string(),\n                })\n            }\n        })?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/mdbx/cursor.rs",
    "content": "use crate::error::Result;\nuse crate::mdbx::db::Db;\nuse crate::mdbx::txn::Txn;\nuse crate::mdbx::{from_mdb_val, mdbx_result, to_mdb_val, Key, KeyVal, EMPTY_KEY, EMPTY_VAL};\nuse core::ptr;\nuse std::cmp::Ordering;\nuse std::marker::PhantomData;\n\npub struct UnboundCursor {\n    cursor: *mut ffi::MDBX_cursor,\n}\n\nimpl UnboundCursor {\n    pub(crate) fn new() -> Self {\n        let cursor = unsafe { ffi::mdbx_cursor_create(ptr::null_mut()) };\n\n        UnboundCursor { cursor }\n    }\n\n    pub fn bind<'txn>(self, txn: &'txn Txn, db: Db) -> Result<Cursor<'txn>> {\n        unsafe {\n            mdbx_result(ffi::mdbx_cursor_bind(txn.txn, self.cursor, db.dbi))?;\n        }\n\n        Ok(Cursor {\n            cursor: self,\n            _marker: PhantomData::default(),\n        })\n    }\n}\n\nimpl Drop for UnboundCursor {\n    fn drop(&mut self) {\n        unsafe { ffi::mdbx_cursor_close(self.cursor) }\n    }\n}\n\npub struct Cursor<'txn> {\n    cursor: UnboundCursor,\n    _marker: PhantomData<&'txn ()>,\n}\n\nimpl<'txn> Cursor<'txn> {\n    pub fn unbind(self) -> UnboundCursor {\n        self.cursor\n    }\n\n    #[allow(clippy::try_err)]\n    fn op_get(\n        &mut self,\n        op: ffi::MDBX_cursor_op,\n        key: Option<&[u8]>,\n        val: Option<&[u8]>,\n    ) -> Result<Option<KeyVal<'txn>>> {\n        let mut key = key.map_or(EMPTY_KEY, |key| unsafe { to_mdb_val(key) });\n        let mut data = val.map_or(EMPTY_VAL, |val| unsafe { to_mdb_val(val) });\n\n        let result = unsafe { ffi::mdbx_cursor_get(self.cursor.cursor, &mut key, &mut data, op) };\n\n        match result {\n            ffi::MDBX_SUCCESS | ffi::MDBX_RESULT_TRUE => {\n                let key = unsafe { from_mdb_val(&key) };\n                let data = unsafe { from_mdb_val(&data) };\n                Ok(Some((key, data)))\n            }\n            ffi::MDBX_NOTFOUND | ffi::MDBX_ENODATA => Ok(None),\n            e => {\n                mdbx_result(e)?;\n                unreachable!();\n            }\n        }\n    }\n\n    pub fn move_to<K: Key>(&mut self, key: &K) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(\n            ffi::MDBX_cursor_op::MDBX_SET_KEY,\n            Some(&key.as_bytes()),\n            None,\n        )\n    }\n\n    pub fn move_to_key_val<K: Key>(&mut self, key: &K, val: &[u8]) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(\n            ffi::MDBX_cursor_op::MDBX_GET_BOTH,\n            Some(&key.as_bytes()),\n            Some(val),\n        )\n    }\n\n    fn move_to_gte<K: Key>(&mut self, key: &K) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(\n            ffi::MDBX_cursor_op::MDBX_SET_RANGE,\n            Some(&key.as_bytes()),\n            None,\n        )\n    }\n\n    fn move_to_next_dup(&mut self) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(ffi::MDBX_cursor_op::MDBX_NEXT_DUP, None, None)\n    }\n\n    fn move_to_last_dup(&mut self) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(ffi::MDBX_cursor_op::MDBX_LAST_DUP, None, None)\n    }\n\n    fn move_to_prev_no_dup(&mut self) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(ffi::MDBX_cursor_op::MDBX_PREV_NODUP, None, None)\n    }\n\n    pub fn move_to_next(&mut self) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(ffi::MDBX_cursor_op::MDBX_NEXT, None, None)\n    }\n\n    pub fn move_to_first(&mut self) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(ffi::MDBX_cursor_op::MDBX_FIRST, None, None)\n    }\n\n    pub fn move_to_last(&mut self) -> Result<Option<KeyVal<'txn>>> {\n        self.op_get(ffi::MDBX_cursor_op::MDBX_LAST, None, None)\n    }\n\n    pub fn put<K: Key>(&mut self, key: &K, data: &[u8]) -> Result<()> {\n        unsafe {\n            // make sure that bytes are not dropped before the call to mdbx_cursor_put\n            let bytes = &key.as_bytes();\n            let key = to_mdb_val(bytes);\n            let mut data = to_mdb_val(data);\n            mdbx_result(ffi::mdbx_cursor_put(self.cursor.cursor, &key, &mut data, 0))?;\n        }\n        Ok(())\n    }\n\n    /// Requires the cursor to have a valid position\n    pub fn delete_current(&mut self) -> Result<()> {\n        unsafe { mdbx_result(ffi::mdbx_cursor_del(self.cursor.cursor, 0))? };\n\n        Ok(())\n    }\n\n    fn iter(\n        &mut self,\n        skip_duplicates: bool,\n        ascending: bool,\n        mut callback: impl FnMut(&mut Self, &'txn [u8], &'txn [u8]) -> Result<bool>,\n    ) -> Result<bool> {\n        let next = match (ascending, skip_duplicates) {\n            (true, true) => ffi::MDBX_cursor_op::MDBX_NEXT_NODUP,\n            (true, false) => ffi::MDBX_cursor_op::MDBX_NEXT,\n            (false, true) => ffi::MDBX_cursor_op::MDBX_PREV_NODUP,\n            (false, false) => ffi::MDBX_cursor_op::MDBX_PREV,\n        };\n        loop {\n            if let Some((key, val)) = self.op_get(next, None, None)? {\n                if !callback(self, key, val)? {\n                    return Ok(false);\n                }\n            } else {\n                return Ok(true);\n            }\n        }\n    }\n\n    pub fn iter_all(\n        &mut self,\n        skip_duplicates: bool,\n        ascending: bool,\n        mut callback: impl FnMut(&mut Self, &'txn [u8], &'txn [u8]) -> Result<bool>,\n    ) -> Result<bool> {\n        let first = if ascending {\n            self.move_to_first()?\n        } else {\n            self.move_to_last()?\n        };\n\n        if let Some((key, val)) = first {\n            if !callback(self, key, val)? {\n                return Ok(false);\n            }\n        } else {\n            return Ok(true);\n        }\n\n        self.iter(skip_duplicates, ascending, callback)\n    }\n\n    fn iter_between_first<K: Key>(\n        &mut self,\n        lower_key: &K,\n        upper_key: &K,\n        ascending: bool,\n        duplicates: bool,\n    ) -> Result<Option<KeyVal<'txn>>> {\n        let first_entry = if !ascending {\n            if let Some(first_entry) = self.move_to_gte(upper_key)? {\n                if duplicates {\n                    self.move_to_last_dup()?.or(Some(first_entry))\n                } else {\n                    Some(first_entry)\n                }\n            } else if let Some(last) = self.move_to_last()? {\n                // If some key between upper_key and lower_key happens to be the last key in the db\n                if lower_key.cmp_bytes(&last.0) != Ordering::Greater {\n                    Some(last)\n                } else {\n                    None\n                }\n            } else {\n                None\n            }\n        } else {\n            self.move_to_gte(lower_key)?\n        };\n\n        if let Some(first_entry) = first_entry {\n            if upper_key.cmp_bytes(&first_entry.0) == Ordering::Less {\n                if !ascending {\n                    if let Some(prev) = self.move_to_prev_no_dup()? {\n                        if lower_key.cmp_bytes(&prev.0) != Ordering::Greater {\n                            return Ok(Some(prev));\n                        }\n                    }\n                }\n                Ok(None)\n            } else {\n                Ok(Some(first_entry))\n            }\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn iter_between<K: Key>(\n        &mut self,\n        lower_key: &K,\n        upper_key: &K,\n        duplicates: bool,\n        skip_duplicates: bool,\n        ascending: bool,\n        mut callback: impl FnMut(&mut Self, &'txn [u8], &'txn [u8]) -> Result<bool>,\n    ) -> Result<bool> {\n        if upper_key.cmp_bytes(&lower_key.as_bytes()) == Ordering::Less {\n            return Ok(true);\n        }\n\n        if let Some((key, val)) =\n            self.iter_between_first(lower_key, upper_key, ascending, duplicates)?\n        {\n            if !callback(self, key, val)? {\n                return Ok(false);\n            }\n        } else {\n            return Ok(true);\n        }\n\n        self.iter(skip_duplicates, ascending, |cursor, key, val| {\n            let abort = if ascending {\n                upper_key.cmp_bytes(&key) == Ordering::Less\n            } else {\n                lower_key.cmp_bytes(&key) == Ordering::Greater\n            };\n            if abort {\n                Ok(true)\n            } else {\n                callback(cursor, key, val)\n            }\n        })\n    }\n\n    pub fn iter_dups<K: Key>(\n        &mut self,\n        key: &K,\n        mut callback: impl FnMut(&mut Self, &'txn [u8]) -> Result<bool>,\n    ) -> Result<bool> {\n        if let Some((_, val)) = self.move_to(key)? {\n            if !callback(self, &val)? {\n                return Ok(false);\n            }\n        } else {\n            return Ok(true);\n        }\n        loop {\n            if let Some((_, val)) = self.move_to_next_dup()? {\n                if !callback(self, &val)? {\n                    return Ok(false);\n                }\n            } else {\n                return Ok(true);\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    /*use crate::mdbx::db::Db;\n    use crate::mdbx::env::tests::get_env;\n    use crate::mdbx::env::Env;\n    use itertools::Itertools;\n    use std::sync::{Arc, Mutex};\n\n    fn get_filled_db() -> (Env, Db) {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", false, false).unwrap();\n        db.put(&txn, b\"key1\", b\"val1\").unwrap();\n        db.put(&txn, b\"key2\", b\"val2\").unwrap();\n        db.put(&txn, b\"key3\", b\"val3\").unwrap();\n        db.put(&txn, b\"key4\", b\"val4\").unwrap();\n        txn.commit().unwrap();\n        (env, db)\n    }\n\n    fn get_filled_db_dup() -> (Env, Db) {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", true, false).unwrap();\n        db.put(&txn, b\"key1\", b\"val1\").unwrap();\n        db.put(&txn, b\"key1\", b\"val1b\").unwrap();\n        db.put(&txn, b\"key1\", b\"val1c\").unwrap();\n        db.put(&txn, b\"key2\", b\"val2\").unwrap();\n        db.put(&txn, b\"key2\", b\"val2b\").unwrap();\n        db.put(&txn, b\"key2\", b\"val2c\").unwrap();\n        txn.commit().unwrap();\n        (env, db)\n    }\n\n    fn get_empty_db() -> (Env, Db) {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", true, false).unwrap();\n        txn.commit().unwrap();\n        (env, db)\n    }\n\n    #[test]\n    fn test_get() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        let entry = cur.get().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1\"[..])));\n\n        cur.move_to_next().unwrap();\n        let entry = cur.get().unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_get_dup() {\n        let (env, db) = get_filled_db_dup();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        let entry = cur.get().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1\"[..])));\n\n        cur.move_to_next().unwrap();\n        let entry = cur.get().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1b\"[..])));\n    }\n\n    #[test]\n    fn test_move_to_first() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let first = cur.move_to_first().unwrap();\n        assert_eq!(first, Some((&b\"key1\"[..], &b\"val1\"[..])));\n\n        let next = cur.move_to_next().unwrap();\n        assert_eq!(next, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_move_to_first_empty() {\n        let (env, db) = get_empty_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let first = cur.move_to_first().unwrap();\n        assert_eq!(first, None);\n\n        let next = cur.move_to_next().unwrap();\n        assert_eq!(next, None);\n    }\n\n    #[test]\n    fn test_move_to_last() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let last = cur.move_to_last().unwrap();\n        assert_eq!(last, Some((&b\"key4\"[..], &b\"val4\"[..])));\n\n        let next = cur.move_to_next().unwrap();\n        assert_eq!(next, None);\n    }\n\n    #[test]\n    fn test_move_to_last_dup() {\n        let (env, db) = get_filled_db_dup();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let last = cur.move_to_last().unwrap();\n        assert_eq!(last, Some((&b\"key2\"[..], &b\"val2c\"[..])));\n    }\n\n    #[test]\n    fn test_move_to_last_empty() {\n        let (env, db) = get_empty_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to_last().unwrap();\n        assert!(entry.is_none());\n\n        let entry = cur.move_to_next().unwrap();\n        assert!(entry.is_none());\n    }\n\n    #[test]\n    fn test_move_to() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to(b\"key2\").unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n\n        let entry = cur.move_to(b\"key1\").unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1\"[..])));\n\n        let next = cur.move_to_next().unwrap();\n        assert_eq!(next, Some((&b\"key2\"[..], &b\"val2\"[..])));\n\n        let entry = cur.move_to(b\"key5\").unwrap();\n        assert_eq!(entry, None);\n    }\n\n    #[test]\n    fn test_move_to_empty() {\n        let (env, db) = get_empty_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to(b\"key1\").unwrap();\n        assert!(entry.is_none());\n        let entry = cur.move_to_next().unwrap();\n        assert!(entry.is_none());\n    }\n\n    #[test]\n    fn test_move_to_gte() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to_gte(b\"key2\").unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n\n        let entry = cur.move_to_gte(b\"k\").unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1\"[..])));\n\n        let next = cur.move_to_next().unwrap();\n        assert_eq!(next, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn move_to_gte_empty() {\n        let (env, db) = get_empty_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to_gte(b\"key1\").unwrap();\n        assert!(entry.is_none());\n\n        let entry = cur.move_to_next().unwrap();\n        assert!(entry.is_none());\n    }\n\n    #[test]\n    fn test_move_to_next() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1\"[..])));\n\n        let entry = cur.move_to_next().unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_move_to_next_dup() {\n        let (env, db) = get_filled_db_dup();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        let entry = cur.move_to_next().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1b\"[..])));\n\n        let entry = cur.move_to_next().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1c\"[..])));\n\n        let entry = cur.move_to_next().unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_move_to_next_empty() {\n        let (env, db) = get_empty_db();\n\n        let txn = env.txn(false).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entry = cur.move_to_next().unwrap();\n        assert!(entry.is_none());\n\n        let entry = cur.move_to_next().unwrap();\n        assert!(entry.is_none());\n    }\n\n    #[test]\n    fn test_delete_current() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(true).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        cur.delete_current(false).unwrap();\n\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_delete_current_dup() {\n        let (env, db) = get_filled_db_dup();\n\n        let txn = env.txn(true).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        cur.delete_current(false).unwrap();\n\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1b\"[..])));\n\n        cur.delete_current(true).unwrap();\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_delete_while() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(true).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        let entries = Arc::new(Mutex::new(vec![(b\"key1\", b\"val1\"), (b\"key2\", b\"val2\")]));\n\n        cur.move_to_first().unwrap();\n        cur.delete_while(\n            |k, v| {\n                let mut entries = entries.lock().unwrap();\n                if entries.is_empty() {\n                    return false;\n                }\n                let (rk, rv) = entries.remove(0);\n                assert_eq!((&rk[..], &rv[..]), (k, v));\n                true\n            },\n            false,\n        )\n        .unwrap();\n\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key3\"[..], &b\"val3\"[..])));\n    }\n\n    #[test]\n    fn test_delete_while_dup() {\n        let (env, db) = get_filled_db_dup();\n\n        let txn = env.txn(true).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        cur.delete_current(false).unwrap();\n\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key1\"[..], &b\"val1b\"[..])));\n\n        cur.delete_current(true).unwrap();\n        let entry = cur.move_to_first().unwrap();\n        assert_eq!(entry, Some((&b\"key2\"[..], &b\"val2\"[..])));\n    }\n\n    #[test]\n    fn test_iter() {\n        let (env, db) = get_filled_db();\n\n        let txn = env.txn(true).unwrap();\n        let mut cur = db.cursor(&txn).unwrap();\n\n        cur.move_to_first().unwrap();\n        cur.move_to_next().unwrap();\n        let keys = cur\n            .iter()\n            .map(|r| {\n                let (k, _) = r.unwrap();\n                k\n            })\n            .collect_vec();\n        assert_eq!(vec![b\"key2\", b\"key3\", b\"key4\"], keys);\n    }\n\n    #[test]\n    fn test_get_put_delete() {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", false, false).unwrap();\n        db.put(&txn, b\"key1\", b\"val1\").unwrap();\n        db.put(&txn, b\"key2\", b\"val2\").unwrap();\n        db.put(&txn, b\"key3\", b\"val3\").unwrap();\n        db.put(&txn, b\"key2\", b\"val4\").unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        assert_eq!(b\"val1\", db.get(&txn, b\"key1\").unwrap().unwrap());\n        assert_eq!(b\"val4\", db.get(&txn, b\"key2\").unwrap().unwrap());\n        assert_eq!(b\"val3\", db.get(&txn, b\"key3\").unwrap().unwrap());\n        assert_eq!(db.get(&txn, b\"key\").unwrap(), None);\n\n        db.delete(&txn, b\"key1\", None).unwrap();\n        assert_eq!(db.get(&txn, b\"key1\").unwrap(), None);\n        txn.abort();\n    }\n\n    #[test]\n    fn test_put_get_del_multi() {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", true, false).unwrap();\n\n        db.put(&txn, b\"key1\", b\"val1\").unwrap();\n        db.put(&txn, b\"key1\", b\"val2\").unwrap();\n        db.put(&txn, b\"key1\", b\"val3\").unwrap();\n        db.put(&txn, b\"key2\", b\"val4\").unwrap();\n        db.put(&txn, b\"key2\", b\"val5\").unwrap();\n        db.put(&txn, b\"key2\", b\"val6\").unwrap();\n        db.put(&txn, b\"key3\", b\"val7\").unwrap();\n        db.put(&txn, b\"key3\", b\"val8\").unwrap();\n        db.put(&txn, b\"key3\", b\"val9\").unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        {\n            //let mut cur = db.cursor(&txn).unwrap();\n            //assert_eq!(cur.set(b\"key2\").unwrap(), true);\n            //let iter = cur.iter_dup();\n            //let vals = iter.map(|x| x.1).collect_vec();\n            //assert!(iter.error.is_none());\n            //assert_eq!(vals, vec![b\"val4\", b\"val5\", b\"val6\"]);\n        }\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        db.delete(&txn, b\"key1\", Some(b\"val2\")).unwrap();\n        db.delete(&txn, b\"key2\", None).unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        {\n            let mut cur = db.cursor(&txn).unwrap();\n            cur.move_to_first().unwrap();\n            let iter = cur.iter();\n            let vals: Result<Vec<&[u8]>> = iter.map_ok(|x| x.1).collect();\n            assert_eq!(\n                vals.unwrap(),\n                vec![b\"val1\", b\"val3\", b\"val7\", b\"val8\", b\"val9\"]\n            );\n        }\n        txn.commit().unwrap();\n    }\n\n    #[test]\n    fn test_put_no_override() {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", false, false).unwrap();\n        db.put(&txn, b\"key\", b\"val\").unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        assert_eq!(db.put_no_override(&txn, b\"key\", b\"err\").unwrap(), false);\n        assert_eq!(db.put_no_override(&txn, b\"key2\", b\"val2\").unwrap(), true);\n        assert_eq!(db.get(&txn, b\"key\").unwrap(), Some(&b\"val\"[..]));\n        assert_eq!(db.get(&txn, b\"key2\").unwrap(), Some(&b\"val2\"[..]));\n        txn.abort();\n    }\n\n    #[test]\n    fn test_put_no_dup_data() {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", true, false).unwrap();\n        db.put(&txn, b\"key\", b\"val\").unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        assert_eq!(db.put_no_dup_data(&txn, b\"key\", b\"val\").unwrap(), false);\n        assert_eq!(db.put_no_dup_data(&txn, b\"key2\", b\"val2\").unwrap(), true);\n        assert_eq!(db.get(&txn, b\"key2\").unwrap(), Some(&b\"val2\"[..]));\n        txn.abort();\n    }\n\n    #[test]\n    fn test_clear_db() {\n        let env = get_env();\n        let txn = env.txn(true).unwrap();\n        let db = Db::open(&txn, \"test\", false, false).unwrap();\n        db.put(&txn, b\"key1\", b\"val1\").unwrap();\n        db.put(&txn, b\"key2\", b\"val2\").unwrap();\n        db.put(&txn, b\"key3\", b\"val3\").unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(true).unwrap();\n        db.clear(&txn).unwrap();\n        txn.commit().unwrap();\n\n        let txn = env.txn(false).unwrap();\n        {\n            let mut cursor = db.cursor(&txn).unwrap();\n            assert!(cursor.move_to_first().unwrap().is_none());\n        }\n        txn.abort();\n    }*/\n}\n"
  },
  {
    "path": "packages/isar_core/src/mdbx/db.rs",
    "content": "use crate::error::Result;\nuse crate::mdbx::mdbx_result;\nuse crate::mdbx::txn::Txn;\nuse std::ffi::CString;\nuse std::mem::size_of;\nuse std::ptr;\n\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct Db {\n    pub(crate) dbi: ffi::MDBX_dbi,\n    pub dup: bool,\n}\n\nimpl Db {\n    pub fn runtime_id(&self) -> u64 {\n        self.dbi as u64\n    }\n\n    pub fn open(\n        txn: &Txn,\n        name: Option<&str>,\n        int_key: bool,\n        dup: bool,\n        int_dup: bool,\n    ) -> Result<Self> {\n        let mut flags = ffi::MDBX_CREATE;\n        if int_key {\n            flags |= ffi::MDBX_INTEGERKEY;\n        }\n        if dup {\n            flags |= ffi::MDBX_DUPSORT;\n            if int_dup {\n                flags |= ffi::MDBX_INTEGERDUP | ffi::MDBX_DUPFIXED;\n            }\n        }\n\n        let mut dbi: ffi::MDBX_dbi = 0;\n        if let Some(name) = name {\n            let name = CString::new(name.as_bytes()).unwrap();\n            unsafe {\n                mdbx_result(ffi::mdbx_dbi_open(txn.txn, name.as_ptr(), flags, &mut dbi))?;\n            }\n        } else {\n            unsafe {\n                mdbx_result(ffi::mdbx_dbi_open(txn.txn, ptr::null(), 0, &mut dbi))?;\n            }\n        }\n\n        Ok(Self { dbi, dup })\n    }\n\n    pub fn stat(&self, txn: &Txn) -> Result<(u64, u64)> {\n        let mut stat = ffi::MDBX_stat {\n            ms_psize: 0,\n            ms_depth: 0,\n            ms_branch_pages: 0,\n            ms_leaf_pages: 0,\n            ms_overflow_pages: 0,\n            ms_entries: 0,\n            ms_mod_txnid: 0,\n        };\n        let stat_ptr = &mut stat as *mut ffi::MDBX_stat;\n        unsafe {\n            ffi::mdbx_dbi_stat(\n                txn.txn,\n                self.dbi,\n                stat_ptr,\n                size_of::<ffi::MDBX_stat>() as ffi::size_t,\n            );\n        }\n        let size = (stat.ms_branch_pages + stat.ms_leaf_pages + stat.ms_overflow_pages)\n            * stat.ms_psize as u64;\n        Ok((stat.ms_entries, size))\n    }\n\n    pub fn clear(&self, txn: &Txn) -> Result<()> {\n        unsafe { mdbx_result(ffi::mdbx_drop(txn.txn, self.dbi, false)) }?;\n        Ok(())\n    }\n\n    pub fn drop(self, txn: &Txn) -> Result<()> {\n        unsafe { mdbx_result(ffi::mdbx_drop(txn.txn, self.dbi, true)) }?;\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    /*#[test]\n    fn test_open() {\n        let env = get_env();\n\n        let read_txn = env.txn(false).unwrap();\n        assert!(Db::open(&read_txn, \"test\", false, false).is_err());\n        read_txn.abort();\n\n        let flags = vec![\n            (false, false, 0),\n            (false, true, 0),\n            (true, false, ffi::MDB_DUPSORT),\n            (true, true, ffi::MDB_DUPSORT | ffi::MDB_DUPFIXED),\n        ];\n\n        for (i, (dup, fixed_vals, flags)) in flags.iter().enumerate() {\n            let txn = env.txn(true).unwrap();\n            let db = Db::open(&txn, format!(\"test{}\", i).as_str(), *dup, *fixed_vals).unwrap();\n            txn.commit().unwrap();\n\n            let mut actual_flags: u32 = 0;\n            let txn = env.txn(false).unwrap();\n            unsafe {\n                ffi::mdb_dbi_flags(txn.txn, db.dbi, &mut actual_flags);\n            }\n            txn.abort();\n            assert_eq!(*flags, actual_flags);\n        }\n    }*/\n}\n"
  },
  {
    "path": "packages/isar_core/src/mdbx/env.rs",
    "content": "use super::osal::*;\nuse crate::error::{IsarError, Result};\nuse crate::mdbx::mdbx_result;\nuse crate::mdbx::txn::Txn;\nuse core::ptr;\n\npub struct Env {\n    env: *mut ffi::MDBX_env,\n}\n\nunsafe impl Sync for Env {}\nunsafe impl Send for Env {}\n\nconst MIB: isize = 1 << 20;\n\nimpl Env {\n    pub fn create(\n        path: &str,\n        max_dbs: u64,\n        max_size_mib: usize,\n        relaxed_durability: bool,\n    ) -> Result<Env> {\n        let path = str_to_os(path)?;\n        let mut env: *mut ffi::MDBX_env = ptr::null_mut();\n        unsafe {\n            mdbx_result(ffi::mdbx_env_create(&mut env))?;\n            mdbx_result(ffi::mdbx_env_set_option(\n                env,\n                ffi::MDBX_option_t::MDBX_opt_max_db,\n                max_dbs,\n            ))?;\n\n            let mut flags = ffi::MDBX_NOTLS | ffi::MDBX_COALESCE | ffi::MDBX_NOSUBDIR;\n            if relaxed_durability {\n                flags |= ffi::MDBX_NOMETASYNC;\n            }\n\n            let max_size = (max_size_mib as isize).saturating_mul(MIB);\n\n            let mut err_code = 0;\n            for i in 0..9 {\n                let max_size_i = (max_size - i * (max_size / 10)).clamp(10 * MIB, isize::MAX);\n                mdbx_result(ffi::mdbx_env_set_geometry(\n                    env,\n                    MIB,\n                    0,\n                    max_size_i,\n                    5 * MIB,\n                    20 * MIB,\n                    -1,\n                ))?;\n\n                err_code = ENV_OPEN(env, path.as_ptr(), flags, 0o600);\n                if err_code == ffi::MDBX_SUCCESS {\n                    break;\n                }\n            }\n\n            match err_code {\n                ffi::MDBX_SUCCESS => Ok(Env { env }),\n                ffi::MDBX_EPERM | ffi::MDBX_ENOFILE => Err(IsarError::PathError {}),\n                e => {\n                    mdbx_result(e)?;\n                    unreachable!()\n                }\n            }\n        }\n    }\n\n    pub fn txn(&self, write: bool) -> Result<Txn> {\n        let flags = if write { 0 } else { ffi::MDBX_TXN_RDONLY };\n        let mut txn: *mut ffi::MDBX_txn = ptr::null_mut();\n        unsafe {\n            mdbx_result(ffi::mdbx_txn_begin_ex(\n                self.env,\n                ptr::null_mut(),\n                flags,\n                &mut txn,\n                ptr::null_mut(),\n            ))?;\n        }\n        Ok(Txn::new(txn, write))\n    }\n\n    pub fn copy(&self, path: &str) -> Result<()> {\n        let path = str_to_os(path)?;\n        unsafe { mdbx_result(ENV_COPY(self.env, path.as_ptr(), ffi::MDBX_CP_COMPACT)) }\n    }\n}\n\nimpl Drop for Env {\n    fn drop(&mut self) {\n        if !self.env.is_null() {\n            unsafe {\n                ffi::mdbx_env_close_ex(self.env, false);\n            }\n            self.env = ptr::null_mut();\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/mdbx/mod.rs",
    "content": "#![allow(clippy::missing_safety_doc)]\n\nuse crate::error::{IsarError, Result};\nuse core::slice;\nuse libc::c_int;\nuse std::borrow::Cow;\nuse std::cmp::Ordering;\nuse std::ffi::{c_void, CStr};\n\npub mod cursor;\npub mod db;\npub mod env;\npub mod txn;\n\npub type KeyVal<'txn> = (&'txn [u8], &'txn [u8]);\n\npub const EMPTY_KEY: ffi::MDBX_val = ffi::MDBX_val {\n    iov_len: 0,\n    iov_base: 0 as *mut c_void,\n};\n\npub const EMPTY_VAL: ffi::MDBX_val = ffi::MDBX_val {\n    iov_len: 0,\n    iov_base: 0 as *mut c_void,\n};\n\n#[inline]\npub unsafe fn from_mdb_val<'a>(val: &ffi::MDBX_val) -> &'a [u8] {\n    slice::from_raw_parts(val.iov_base as *const u8, val.iov_len as usize)\n}\n\n#[inline]\npub unsafe fn to_mdb_val(value: &[u8]) -> ffi::MDBX_val {\n    ffi::MDBX_val {\n        iov_len: value.len() as ffi::size_t,\n        iov_base: value.as_ptr() as *mut libc::c_void,\n    }\n}\n\n#[inline]\npub fn mdbx_result(err_code: c_int) -> Result<()> {\n    match err_code {\n        ffi::MDBX_SUCCESS | ffi::MDBX_RESULT_TRUE => Ok(()),\n        ffi::MDBX_MAP_FULL => Err(IsarError::DbFull {}),\n        other => unsafe {\n            let err_raw = ffi::mdbx_strerror(other);\n            let err = CStr::from_ptr(err_raw);\n            Err(IsarError::MdbxError {\n                code: other,\n                message: err\n                    .to_str()\n                    .unwrap_or(\"Cannot decode error message\")\n                    .to_string(),\n            })\n        },\n    }\n}\n\npub trait Key {\n    fn as_bytes(&self) -> Cow<[u8]>;\n\n    fn cmp_bytes(&self, other: &[u8]) -> Ordering;\n}\n\n#[cfg(target_os = \"windows\")]\npub(crate) mod osal {\n    use super::*;\n    use widestring::U16CString;\n\n    pub fn str_to_os(str: &str) -> Result<U16CString> {\n        U16CString::from_str(str).map_err(|_| IsarError::IllegalArg {\n            message: \"Invalid String provided\".to_string(),\n        })\n    }\n\n    pub const ENV_OPEN: unsafe extern \"C\" fn(\n        *mut ffi::MDBX_env,\n        *const u16,\n        ffi::MDBX_env_flags_t,\n        ffi::mdbx_mode_t,\n    ) -> i32 = ffi::mdbx_env_openW;\n\n    pub const ENV_COPY: unsafe extern \"C\" fn(\n        *mut ffi::MDBX_env,\n        *const u16,\n        ffi::MDBX_copy_flags_t,\n    ) -> i32 = ffi::mdbx_env_copyW;\n}\n\n#[cfg(not(target_os = \"windows\"))]\npub(crate) mod osal {\n    use super::*;\n    use std::ffi::CString;\n\n    pub fn str_to_os(str: &str) -> Result<CString> {\n        CString::new(str.as_bytes()).map_err(|_| IsarError::IllegalArg {\n            message: \"Invalid String provided\".to_string(),\n        })\n    }\n\n    pub const ENV_OPEN: unsafe extern \"C\" fn(\n        *mut ffi::MDBX_env,\n        *const libc::c_char,\n        ffi::MDBX_env_flags_t,\n        ffi::mdbx_mode_t,\n    ) -> i32 = ffi::mdbx_env_open;\n\n    pub const ENV_COPY: unsafe extern \"C\" fn(\n        *mut ffi::MDBX_env,\n        *const libc::c_char,\n        ffi::MDBX_copy_flags_t,\n    ) -> i32 = ffi::mdbx_env_copy;\n}\n"
  },
  {
    "path": "packages/isar_core/src/mdbx/txn.rs",
    "content": "use crate::error::Result;\nuse crate::mdbx::mdbx_result;\nuse core::ptr;\nuse std::marker::PhantomData;\n\npub struct Txn<'env> {\n    pub(crate) txn: *mut ffi::MDBX_txn,\n    pub write: bool,\n    _marker: PhantomData<&'env ()>,\n}\n\nimpl<'env> Txn<'env> {\n    pub(crate) fn new(txn: *mut ffi::MDBX_txn, write: bool) -> Self {\n        Txn {\n            txn,\n            write,\n            _marker: PhantomData::default(),\n        }\n    }\n\n    pub fn commit(mut self) -> Result<()> {\n        let result = unsafe { mdbx_result(ffi::mdbx_txn_commit_ex(self.txn, ptr::null_mut())) };\n        self.txn = ptr::null_mut();\n        result?;\n        Ok(())\n    }\n\n    pub fn abort(self) {}\n}\n\nimpl<'a> Drop for Txn<'a> {\n    fn drop(&mut self) {\n        if !self.txn.is_null() {\n            unsafe {\n                ffi::mdbx_txn_abort(self.txn);\n            }\n            self.txn = ptr::null_mut();\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/object/data_type.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Hash)]\npub enum DataType {\n    Bool,\n    Byte,\n    Int,\n    Float,\n    #[serde(alias = \"DateTime\")]\n    Long,\n    Double,\n    String,\n    Object,\n    BoolList,\n    ByteList,\n    IntList,\n    FloatList,\n    #[serde(alias = \"DateTimeList\")]\n    LongList,\n    DoubleList,\n    StringList,\n    ObjectList,\n}\n\nimpl DataType {\n    pub fn is_static(&self) -> bool {\n        matches!(\n            &self,\n            DataType::Bool\n                | DataType::Byte\n                | DataType::Int\n                | DataType::Long\n                | DataType::Float\n                | DataType::Double\n        )\n    }\n\n    pub fn is_dynamic(&self) -> bool {\n        !self.is_static()\n    }\n\n    pub fn get_static_size(&self) -> usize {\n        match *self {\n            DataType::Bool | DataType::Byte => 1,\n            DataType::Int | DataType::Float => 4,\n            DataType::Long | DataType::Double => 8,\n            _ => 3,\n        }\n    }\n\n    pub fn is_scalar(&self) -> bool {\n        self.get_element_type().is_none()\n    }\n\n    pub fn get_element_type(&self) -> Option<DataType> {\n        match self {\n            DataType::BoolList => Some(DataType::Bool),\n            DataType::ByteList => Some(DataType::Byte),\n            DataType::IntList => Some(DataType::Int),\n            DataType::FloatList => Some(DataType::Float),\n            DataType::LongList => Some(DataType::Long),\n            DataType::DoubleList => Some(DataType::Double),\n            DataType::StringList => Some(DataType::String),\n            DataType::ObjectList => Some(DataType::Object),\n            _ => None,\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/object/id.rs",
    "content": "use std::{borrow::Cow, cmp::Ordering};\n\nuse crate::mdbx::Key;\n\npub trait BytesToId {\n    fn to_id(&self) -> i64;\n}\n\nimpl BytesToId for &[u8] {\n    fn to_id(&self) -> i64 {\n        let unsigned = u64::from_le_bytes((**self).try_into().unwrap());\n        let signed: i64 = unsigned as i64;\n        signed ^ 1 << 63\n    }\n}\n\npub trait IdToBytes {\n    fn to_id_bytes(&self) -> [u8; 8];\n}\n\nimpl IdToBytes for i64 {\n    fn to_id_bytes(&self) -> [u8; 8] {\n        let unsigned = *self as u64;\n        (unsigned ^ 1 << 63).to_le_bytes()\n    }\n}\n\nimpl Key for i64 {\n    fn as_bytes(&self) -> Cow<[u8]> {\n        let bytes = self.to_id_bytes();\n        Cow::Owned(bytes.to_vec())\n    }\n\n    fn cmp_bytes(&self, other: &[u8]) -> Ordering {\n        let other_id = other.to_id();\n        self.cmp(&other_id)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_to_id_bytes() {\n        assert_eq!(i64::MIN.to_id_bytes(), [0, 0, 0, 0, 0, 0, 0, 0]);\n        assert_eq!((i64::MIN + 1).to_id_bytes(), [1, 0, 0, 0, 0, 0, 0, 0]);\n        assert_eq!(\n            i64::MAX.to_id_bytes(),\n            [255, 255, 255, 255, 255, 255, 255, 255]\n        );\n        assert_eq!(\n            (i64::MAX - 1).to_id_bytes(),\n            [254, 255, 255, 255, 255, 255, 255, 255]\n        );\n    }\n\n    #[test]\n    fn test_to_id() {\n        assert_eq!([0, 0, 0, 0, 0, 0, 0, 0].as_ref().to_id(), i64::MIN);\n        assert_eq!([1, 0, 0, 0, 0, 0, 0, 0].as_ref().to_id(), i64::MIN + 1);\n        assert_eq!(\n            [254, 255, 255, 255, 255, 255, 255, 255].as_ref().to_id(),\n            i64::MAX - 1\n        );\n        assert_eq!(\n            [255, 255, 255, 255, 255, 255, 255, 255].as_ref().to_id(),\n            i64::MAX\n        );\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/object/isar_object.rs",
    "content": "use crate::object::data_type::DataType;\nuse crate::object::object_builder::ObjectBuilder;\nuse byteorder::{ByteOrder, LittleEndian};\nuse std::{cmp::Ordering, str::from_utf8_unchecked};\nuse xxhash_rust::xxh3::xxh3_64_with_seed;\n\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct IsarObject<'a> {\n    bytes: &'a [u8],\n    static_size: usize,\n}\n\nimpl<'a> IsarObject<'a> {\n    pub const NULL_BYTE: u8 = 0;\n    pub const NULL_BOOL: u8 = 0;\n    pub const FALSE_BOOL: u8 = 1;\n    pub const TRUE_BOOL: u8 = 2;\n    pub const NULL_INT: i32 = i32::MIN;\n    pub const NULL_LONG: i64 = i64::MIN;\n    pub const NULL_FLOAT: f32 = f32::NAN;\n    pub const NULL_DOUBLE: f64 = f64::NAN;\n    pub const MAX_SIZE: u32 = 2 << 24;\n\n    pub fn from_bytes(bytes: &'a [u8]) -> Self {\n        let static_size = LittleEndian::read_u16(bytes) as usize;\n        IsarObject { bytes, static_size }\n    }\n\n    pub fn as_bytes(&self) -> &'a [u8] {\n        self.bytes\n    }\n\n    pub fn len(&self) -> usize {\n        self.bytes.len()\n    }\n\n    #[inline]\n    pub(crate) fn contains_offset(&self, offset: usize) -> bool {\n        self.static_size > offset\n    }\n\n    pub fn is_null(&self, offset: usize, data_type: DataType) -> bool {\n        match data_type {\n            DataType::Byte => false,\n            DataType::Bool => self.read_bool(offset).is_none(),\n            DataType::Int => self.read_int(offset) == Self::NULL_INT,\n            DataType::Long => self.read_long(offset) == Self::NULL_LONG,\n            DataType::Float => self.read_float(offset).is_nan(),\n            DataType::Double => self.read_double(offset).is_nan(),\n            _ => self.get_offset_length(offset).is_none(),\n        }\n    }\n\n    #[inline]\n    pub fn byte_to_bool(value: u8) -> Option<bool> {\n        if value == Self::NULL_BOOL {\n            None\n        } else {\n            Some(value == Self::TRUE_BOOL)\n        }\n    }\n\n    pub fn read_byte(&self, offset: usize) -> u8 {\n        if self.contains_offset(offset) {\n            self.bytes[offset]\n        } else {\n            Self::NULL_BYTE\n        }\n    }\n\n    pub fn read_bool(&self, offset: usize) -> Option<bool> {\n        let value = if self.contains_offset(offset) {\n            self.bytes[offset]\n        } else {\n            Self::NULL_BOOL\n        };\n        Self::byte_to_bool(value)\n    }\n\n    pub fn read_int(&self, offset: usize) -> i32 {\n        if self.contains_offset(offset) {\n            LittleEndian::read_i32(&self.bytes[offset..])\n        } else {\n            Self::NULL_INT\n        }\n    }\n\n    pub fn read_float(&self, offset: usize) -> f32 {\n        if self.contains_offset(offset) {\n            LittleEndian::read_f32(&self.bytes[offset..])\n        } else {\n            Self::NULL_FLOAT\n        }\n    }\n\n    pub fn read_long(&self, offset: usize) -> i64 {\n        if self.contains_offset(offset) {\n            LittleEndian::read_i64(&self.bytes[offset..])\n        } else {\n            Self::NULL_LONG\n        }\n    }\n\n    pub fn read_double(&self, offset: usize) -> f64 {\n        if self.contains_offset(offset) {\n            LittleEndian::read_f64(&self.bytes[offset..])\n        } else {\n            Self::NULL_DOUBLE\n        }\n    }\n\n    fn read_u24(&self, offset: usize) -> usize {\n        LittleEndian::read_u24(&self.bytes[offset..]) as usize\n    }\n\n    fn get_offset_length(&self, offset: usize) -> Option<(usize, usize)> {\n        if self.contains_offset(offset) {\n            let length_offset = self.read_u24(offset);\n            if length_offset != 0 {\n                let length = self.read_u24(length_offset);\n                return Some((length_offset + 3, length));\n            }\n        }\n        None\n    }\n\n    pub fn read_length(&self, offset: usize) -> Option<usize> {\n        let (_, length) = self.get_offset_length(offset)?;\n        Some(length)\n    }\n\n    pub fn read_byte_list(&self, offset: usize) -> Option<&'a [u8]> {\n        let (offset, length) = self.get_offset_length(offset)?;\n        Some(&self.bytes[offset..offset + length])\n    }\n\n    pub fn read_string(&'a self, offset: usize) -> Option<&'a str> {\n        let bytes = self.read_byte_list(offset)?;\n        let str = unsafe { from_utf8_unchecked(bytes) };\n        Some(str)\n    }\n\n    pub fn read_object(&'a self, offset: usize) -> Option<IsarObject> {\n        let bytes = self.read_byte_list(offset)?;\n        Some(IsarObject::from_bytes(bytes))\n    }\n\n    pub fn read_bool_list(&self, offset: usize) -> Option<Vec<Option<bool>>> {\n        let (offset, length) = self.get_offset_length(offset)?;\n        let mut list = vec![None; length];\n        for i in 0..length {\n            list[i] = Self::byte_to_bool(self.bytes[offset + i]);\n        }\n        Some(list)\n    }\n\n    pub fn read_int_list(&self, offset: usize) -> Option<Vec<i32>> {\n        let (offset, length) = self.get_offset_length(offset)?;\n        let mut list = vec![0; length];\n        for i in 0..length {\n            list[i] = LittleEndian::read_i32(&self.bytes[offset + i * 4..]);\n        }\n        Some(list)\n    }\n\n    pub fn read_int_or_null_list(&self, offset: usize) -> Option<Vec<Option<i32>>> {\n        self.read_int_list(offset).map(|list| {\n            list.into_iter()\n                .map(|value| {\n                    if value != Self::NULL_INT {\n                        Some(value)\n                    } else {\n                        None\n                    }\n                })\n                .collect()\n        })\n    }\n\n    pub fn read_float_list(&self, offset: usize) -> Option<Vec<f32>> {\n        let (offset, length) = self.get_offset_length(offset)?;\n        let mut list = vec![0.0; length];\n        for i in 0..length {\n            list[i] = LittleEndian::read_f32(&self.bytes[offset + i * 4..]);\n        }\n        Some(list)\n    }\n\n    pub fn read_float_or_null_list(&self, offset: usize) -> Option<Vec<Option<f32>>> {\n        self.read_float_list(offset).map(|list| {\n            list.into_iter()\n                .map(|value| if !value.is_nan() { Some(value) } else { None })\n                .collect()\n        })\n    }\n\n    pub fn read_long_list(&self, offset: usize) -> Option<Vec<i64>> {\n        let (offset, length) = self.get_offset_length(offset)?;\n        let mut list = vec![0; length];\n        for i in 0..length {\n            list[i] = LittleEndian::read_i64(&self.bytes[offset + i * 8..]);\n        }\n        Some(list)\n    }\n\n    pub fn read_long_or_null_list(&self, offset: usize) -> Option<Vec<Option<i64>>> {\n        self.read_long_list(offset).map(|list| {\n            list.into_iter()\n                .map(|value| {\n                    if value != Self::NULL_LONG {\n                        Some(value)\n                    } else {\n                        None\n                    }\n                })\n                .collect()\n        })\n    }\n\n    pub fn read_double_list(&self, offset: usize) -> Option<Vec<f64>> {\n        let (offset, length) = self.get_offset_length(offset)?;\n        let mut list = vec![0.0; length];\n        for i in 0..length {\n            list[i] = LittleEndian::read_f64(&self.bytes[offset + i * 8..]);\n        }\n        Some(list)\n    }\n\n    pub fn read_double_or_null_list(&self, offset: usize) -> Option<Vec<Option<f64>>> {\n        self.read_double_list(offset).map(|list| {\n            list.into_iter()\n                .map(|value| if !value.is_nan() { Some(value) } else { None })\n                .collect()\n        })\n    }\n\n    pub fn read_string_list(&self, offset: usize) -> Option<Vec<Option<&'a str>>> {\n        self.read_dynamic_list(offset, |bytes| unsafe { from_utf8_unchecked(bytes) })\n    }\n\n    pub fn read_object_list(&self, offset: usize) -> Option<Vec<Option<IsarObject<'a>>>> {\n        self.read_dynamic_list(offset, |bytes| IsarObject::from_bytes(bytes))\n    }\n\n    fn read_dynamic_list<T: Clone>(\n        &self,\n        offset: usize,\n        transform: impl Fn(&'a [u8]) -> T,\n    ) -> Option<Vec<Option<T>>> {\n        let (offset, length) = self.get_offset_length(offset)?;\n\n        let mut list = vec![None; length];\n        let mut content_offset = offset + length * 3;\n        for i in 0..length {\n            let item_size = self.read_u24(offset + i * 3);\n            if item_size != 0 {\n                let item_size = item_size - 1;\n                let bytes = &self.bytes[content_offset..content_offset + item_size];\n                let value = transform(bytes);\n                list[i] = Some(value);\n                content_offset += item_size;\n            }\n        }\n\n        Some(list)\n    }\n\n    pub fn hash_property(\n        &self,\n        offset: usize,\n        data_type: DataType,\n        case_sensitive: bool,\n        seed: u64,\n    ) -> u64 {\n        match data_type {\n            DataType::Bool | DataType::Byte => xxh3_64_with_seed(&[self.read_byte(offset)], seed),\n            DataType::Int => xxh3_64_with_seed(&self.read_int(offset).to_le_bytes(), seed),\n            DataType::Float => xxh3_64_with_seed(&self.read_float(offset).to_le_bytes(), seed),\n            DataType::Long => xxh3_64_with_seed(&self.read_long(offset).to_le_bytes(), seed),\n            DataType::Double => xxh3_64_with_seed(&self.read_double(offset).to_le_bytes(), seed),\n            DataType::String => Self::hash_string(self.read_string(offset), case_sensitive, seed),\n            _ => match data_type {\n                DataType::StringList => {\n                    Self::hash_string_list(self.read_string_list(offset), case_sensitive, seed)\n                }\n                _ => {\n                    if let Some((offset, length)) = self.get_offset_length(offset) {\n                        let element_size = data_type.get_element_type().unwrap().get_static_size();\n                        xxh3_64_with_seed(&self.bytes[offset..offset + length * element_size], seed)\n                    } else {\n                        seed\n                    }\n                }\n            },\n        }\n    }\n\n    pub fn hash_string(value: Option<&str>, case_sensitive: bool, seed: u64) -> u64 {\n        if let Some(str) = value {\n            if case_sensitive {\n                xxh3_64_with_seed(str.as_bytes(), seed)\n            } else {\n                xxh3_64_with_seed(str.to_lowercase().as_bytes(), seed)\n            }\n        } else {\n            seed\n        }\n    }\n\n    pub fn hash_list<T>(value: Option<&[T]>, seed: u64) -> u64 {\n        if let Some(list) = value {\n            let bytes = ObjectBuilder::get_list_bytes(list);\n            xxh3_64_with_seed(bytes, seed)\n        } else {\n            seed\n        }\n    }\n\n    pub fn hash_string_list(\n        value: Option<Vec<Option<&str>>>,\n        case_sensitive: bool,\n        seed: u64,\n    ) -> u64 {\n        if let Some(str) = value {\n            let mut hash = seed;\n            for value in str {\n                hash = Self::hash_string(value, case_sensitive, hash);\n            }\n            hash\n        } else {\n            seed\n        }\n    }\n\n    pub fn compare_property(\n        &self,\n        other: &IsarObject,\n        offset: usize,\n        data_type: DataType,\n    ) -> Ordering {\n        match data_type {\n            DataType::Bool | DataType::Byte => self.read_byte(offset).cmp(&other.read_byte(offset)),\n            DataType::Int => self.read_int(offset).cmp(&other.read_int(offset)),\n            DataType::Float => self.read_float(offset).total_cmp(&other.read_float(offset)),\n            DataType::Long => self.read_long(offset).cmp(&other.read_long(offset)),\n            DataType::Double => self\n                .read_double(offset)\n                .total_cmp(&other.read_double(offset)),\n            DataType::String => self.read_string(offset).cmp(&other.read_string(offset)),\n            _ => Ordering::Equal,\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use itertools::Itertools;\n\n    use crate::object::data_type::DataType::*;\n    use crate::object::isar_object::IsarObject;\n    use crate::object::object_builder::ObjectBuilder;\n    use crate::object::property::Property;\n\n    macro_rules! builder {\n        ($builder:ident, $prop:ident, $type:ident) => {\n            let $prop = Property::debug($type, 2);\n            let props = vec![$prop.clone()];\n            let mut $builder = ObjectBuilder::new(&props, None);\n        };\n    }\n\n    #[test]\n    fn test_read_non_contained_property() {\n        let data_types = vec![\n            Bool, Byte, Int, Float, Long, Double, String, BoolList, ByteList, IntList, FloatList,\n            LongList, DoubleList, StringList,\n        ];\n        for data_type in data_types {\n            builder!(_b, p, data_type);\n            let empty = vec![0, 0];\n            let object = IsarObject::from_bytes(&empty);\n            let should_be_null = data_type != Byte;\n            assert_eq!(object.is_null(p.offset, p.data_type), should_be_null);\n        }\n    }\n\n    #[test]\n    fn test_read_bool() {\n        builder!(b, p, Bool);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_bool(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Bool);\n        b.write_bool(p.offset, Some(true));\n        assert_eq!(b.finish().read_bool(p.offset), Some(true));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Bool);\n        b.write_bool(p.offset, Some(false));\n        assert_eq!(b.finish().read_bool(p.offset), Some(false));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_byte() {\n        builder!(b, p, Byte);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_byte(p.offset), IsarObject::NULL_BYTE);\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Byte);\n        b.write_byte(p.offset, 123);\n        assert_eq!(b.finish().read_byte(p.offset), 123);\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_int() {\n        builder!(b, p, Int);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_int(p.offset), IsarObject::NULL_INT);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Int);\n        b.write_int(p.offset, 123);\n        assert_eq!(b.finish().read_int(p.offset), 123);\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_float() {\n        builder!(b, p, Float);\n        b.write_null(p.offset, p.data_type);\n        assert!(b.finish().read_float(p.offset).is_nan());\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Float);\n        b.write_float(p.offset, 123.123);\n        assert!((b.finish().read_float(p.offset) - 123.123).abs() < 0.000001);\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_long() {\n        builder!(b, p, Long);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_long(p.offset), IsarObject::NULL_LONG);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Long);\n        b.write_long(p.offset, 123123123123123123);\n        assert_eq!(b.finish().read_long(p.offset), 123123123123123123);\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_double() {\n        builder!(b, p, Double);\n        b.write_null(p.offset, p.data_type);\n        assert!(b.finish().read_double(p.offset).is_nan());\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, Double);\n        b.write_double(p.offset, 123123.123123123);\n        assert!((b.finish().read_double(p.offset) - 123123.123123123).abs() < 0.00000001);\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_string() {\n        builder!(b, p, String);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_string(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, String);\n        b.write_string(p.offset, Some(\"hello\"));\n        assert_eq!(b.finish().read_string(p.offset), Some(\"hello\"));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, String);\n        b.write_string(p.offset, Some(\"\"));\n        assert_eq!(b.finish().read_string(p.offset), Some(\"\"));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_byte_list() {\n        builder!(b, p, ByteList);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_byte_list(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, ByteList);\n        b.write_byte_list(p.offset, Some(&[1, 2, 3]));\n        assert_eq!(b.finish().read_byte_list(p.offset), Some(&[1, 2, 3][..]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, ByteList);\n        b.write_byte_list(p.offset, Some(&[]));\n        assert_eq!(b.finish().read_byte_list(p.offset), Some(&[][..]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_int_list() {\n        builder!(b, p, IntList);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_int_list(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, IntList);\n        b.write_int_list(p.offset, Some(&[1, 2, 3]));\n        assert_eq!(b.finish().read_int_list(p.offset), Some(vec![1, 2, 3]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, IntList);\n        b.write_int_list(p.offset, Some(&[]));\n        assert_eq!(b.finish().read_int_list(p.offset), Some(vec![]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_float_list() {\n        builder!(b, p, FloatList);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_float_list(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, FloatList);\n        b.write_float_list(p.offset, Some(&[1.1, 2.2, 3.3]));\n        assert_eq!(\n            b.finish().read_float_list(p.offset),\n            Some(vec![1.1, 2.2, 3.3])\n        );\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, FloatList);\n        b.write_float_list(p.offset, Some(&[]));\n        assert_eq!(b.finish().read_float_list(p.offset), Some(vec![]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_long_list() {\n        builder!(b, p, LongList);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_long_list(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, LongList);\n        b.write_long_list(p.offset, Some(&[1, 2, 3]));\n        assert_eq!(b.finish().read_long_list(p.offset), Some(vec![1, 2, 3]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, LongList);\n        b.write_long_list(p.offset, Some(&[]));\n        assert_eq!(b.finish().read_long_list(p.offset), Some(vec![]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_double_list() {\n        builder!(b, p, DoubleList);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_double_list(p.offset), None);\n        assert!(b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, DoubleList);\n        b.write_double_list(p.offset, Some(&[1.1, 2.2, 3.3]));\n        assert_eq!(\n            b.finish().read_double_list(p.offset),\n            Some(vec![1.1, 2.2, 3.3])\n        );\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n\n        builder!(b, p, DoubleList);\n        b.write_double_list(p.offset, Some(&[]));\n        assert_eq!(b.finish().read_double_list(p.offset), Some(vec![]));\n        assert!(!b.finish().is_null(p.offset, p.data_type));\n    }\n\n    #[test]\n    fn test_read_string_list() {\n        builder!(b, p, StringList);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().read_string_list(p.offset), None);\n\n        let cases = vec![\n            vec![],\n            vec![None],\n            vec![None, None],\n            vec![None, None, None],\n            vec![Some(\"\")],\n            vec![Some(\"\"), Some(\"\")],\n            vec![Some(\"\"), Some(\"\"), Some(\"\")],\n            vec![Some(\"\"), None],\n            vec![None, Some(\"\")],\n            vec![Some(\"\"), None, None],\n            vec![None, Some(\"\"), None],\n            vec![None, None, Some(\"\")],\n            vec![None, Some(\"\"), Some(\"\")],\n            vec![Some(\"\"), None, Some(\"\")],\n            vec![Some(\"\"), Some(\"\"), None],\n            vec![Some(\"a\")],\n            vec![Some(\"a\"), Some(\"ab\")],\n            vec![Some(\"a\"), Some(\"ab\"), Some(\"abc\")],\n            vec![None, Some(\"a\")],\n            vec![Some(\"a\"), None],\n            vec![None, Some(\"a\")],\n            vec![Some(\"a\"), None, None],\n            vec![None, Some(\"a\"), None],\n            vec![None, None, Some(\"a\")],\n            vec![None, Some(\"a\"), Some(\"bbb\")],\n            vec![Some(\"a\"), None, Some(\"bbb\")],\n            vec![Some(\"a\"), Some(\"bbb\"), None],\n        ];\n\n        for case1 in &cases {\n            for case2 in &cases {\n                for case3 in &cases {\n                    let case = case1\n                        .iter()\n                        .chain(case2)\n                        .chain(case3)\n                        .cloned()\n                        .collect_vec();\n                    builder!(b, p, StringList);\n                    b.write_string_list(p.offset, Some(&case));\n                    assert_eq!(b.finish().read_string_list(p.offset), Some(case));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/object/json_encode_decode.rs",
    "content": "use crate::error::{IsarError, Result};\nuse crate::object::data_type::DataType;\nuse crate::object::isar_object::IsarObject;\nuse crate::object::object_builder::ObjectBuilder;\nuse intmap::IntMap;\nuse itertools::Itertools;\nuse serde_json::{json, Map, Value};\n\nuse super::property::Property;\n\npub struct JsonEncodeDecode {}\n\nimpl<'a> JsonEncodeDecode {\n    #[inline(never)]\n    pub fn encode(\n        properties: &[Property],\n        embedded_properties: &IntMap<Vec<Property>>,\n        object: IsarObject,\n        primitive_null: bool,\n    ) -> Map<String, Value> {\n        let mut object_map = Map::new();\n\n        for property in properties {\n            let value = if primitive_null && object.is_null(property.offset, property.data_type) {\n                Value::Null\n            } else {\n                match property.data_type {\n                    DataType::Bool => {\n                        json!(object.read_bool(property.offset))\n                    }\n                    DataType::Byte => {\n                        json!(object.read_byte(property.offset))\n                    }\n                    DataType::Int => json!(object.read_int(property.offset)),\n                    DataType::Float => json!(object.read_float(property.offset)),\n                    DataType::Long => json!(object.read_long(property.offset)),\n                    DataType::Double => json!(object.read_double(property.offset)),\n                    DataType::String => json!(object.read_string(property.offset)),\n                    DataType::Object => {\n                        let properties = embedded_properties\n                            .get(property.target_id.unwrap())\n                            .unwrap();\n                        Self::object_to_value(\n                            properties,\n                            embedded_properties,\n                            object.read_object(property.offset),\n                            primitive_null,\n                        )\n                    }\n                    DataType::BoolList => json!(object.read_bool_list(property.offset).unwrap()),\n                    DataType::ByteList => json!(object.read_byte_list(property.offset).unwrap()),\n                    DataType::IntList => {\n                        if primitive_null {\n                            json!(object.read_int_or_null_list(property.offset))\n                        } else {\n                            json!(object.read_int_list(property.offset))\n                        }\n                    }\n                    DataType::FloatList => {\n                        if primitive_null {\n                            json!(object.read_float_or_null_list(property.offset))\n                        } else {\n                            json!(object.read_float_list(property.offset))\n                        }\n                    }\n                    DataType::LongList => {\n                        if primitive_null {\n                            json!(object.read_long_or_null_list(property.offset))\n                        } else {\n                            json!(object.read_long_list(property.offset))\n                        }\n                    }\n                    DataType::DoubleList => {\n                        if primitive_null {\n                            json!(object.read_double_or_null_list(property.offset))\n                        } else {\n                            json!(object.read_double_list(property.offset))\n                        }\n                    }\n                    DataType::StringList => json!(object.read_string_list(property.offset)),\n                    DataType::ObjectList => {\n                        let properties = embedded_properties\n                            .get(property.target_id.unwrap())\n                            .unwrap();\n                        if let Some(objects) = object.read_object_list(property.offset) {\n                            let encoded = objects\n                                .into_iter()\n                                .map(|object| {\n                                    Self::object_to_value(\n                                        properties,\n                                        embedded_properties,\n                                        object,\n                                        primitive_null,\n                                    )\n                                })\n                                .collect_vec();\n\n                            json!(encoded)\n                        } else {\n                            Value::Null\n                        }\n                    }\n                }\n            };\n            object_map.insert(property.name.clone(), value);\n        }\n\n        object_map\n    }\n\n    fn object_to_value(\n        properties: &[Property],\n        embedded_properties: &IntMap<Vec<Property>>,\n        object: Option<IsarObject>,\n        primitive_null: bool,\n    ) -> Value {\n        if let Some(object) = object {\n            let encoded =\n                JsonEncodeDecode::encode(properties, embedded_properties, object, primitive_null);\n            json!(encoded)\n        } else {\n            Value::Null\n        }\n    }\n\n    #[inline(never)]\n    pub fn decode(\n        properties: &[Property],\n        embedded_properties: &IntMap<Vec<Property>>,\n        ob: &mut ObjectBuilder,\n        json: &Value,\n    ) -> Result<()> {\n        let object = json.as_object().ok_or(IsarError::InvalidJson {})?;\n\n        for property in properties {\n            if let Some(value) = object.get(&property.name) {\n                match property.data_type {\n                    DataType::Bool => ob.write_bool(property.offset, Self::value_to_bool(value)?),\n                    DataType::Byte => ob.write_byte(property.offset, Self::value_to_byte(value)?),\n                    DataType::Int => ob.write_int(property.offset, Self::value_to_int(value)?),\n                    DataType::Float => {\n                        ob.write_float(property.offset, Self::value_to_float(value)?)\n                    }\n                    DataType::Long => ob.write_long(property.offset, Self::value_to_long(value)?),\n                    DataType::Double => {\n                        ob.write_double(property.offset, Self::value_to_double(value)?)\n                    }\n                    DataType::String => {\n                        ob.write_string(property.offset, Self::value_to_string(value)?)\n                    }\n                    DataType::Object => {\n                        let builder = Self::value_to_object(\n                            value,\n                            embedded_properties,\n                            property.target_id.unwrap(),\n                        )?;\n                        ob.write_object(property.offset, builder.as_ref().map(|b| b.finish()));\n                    }\n                    DataType::BoolList => {\n                        let list = Self::value_to_array(value, Self::value_to_bool)?;\n                        ob.write_bool_list(property.offset, list.as_deref());\n                    }\n                    DataType::ByteList => {\n                        let list = Self::value_to_array(value, Self::value_to_byte)?;\n                        ob.write_byte_list(property.offset, list.as_deref());\n                    }\n                    DataType::IntList => {\n                        let list = Self::value_to_array(value, Self::value_to_int)?;\n                        ob.write_int_list(property.offset, list.as_deref());\n                    }\n                    DataType::FloatList => {\n                        let list = Self::value_to_array(value, Self::value_to_float)?;\n                        ob.write_float_list(property.offset, list.as_deref());\n                    }\n                    DataType::LongList => {\n                        let list = Self::value_to_array(value, Self::value_to_long)?;\n                        ob.write_long_list(property.offset, list.as_deref());\n                    }\n                    DataType::DoubleList => {\n                        let list = Self::value_to_array(value, Self::value_to_double)?;\n                        ob.write_double_list(property.offset, list.as_deref());\n                    }\n                    DataType::StringList => {\n                        if value.is_null() {\n                            ob.write_string_list(property.offset, None);\n                        } else if let Some(list) = value.as_array() {\n                            let list: Result<Vec<Option<&str>>> =\n                                list.iter().map(Self::value_to_string).collect();\n                            ob.write_string_list(property.offset, Some(list?.as_slice()));\n                        } else {\n                            return Err(IsarError::InvalidJson {});\n                        }\n                    }\n                    DataType::ObjectList => {\n                        if value.is_null() {\n                            ob.write_object_list(property.offset, None);\n                        } else if let Some(list) = value.as_array() {\n                            let list: Result<Vec<Option<ObjectBuilder>>> = list\n                                .iter()\n                                .map(|value| {\n                                    Self::value_to_object(\n                                        value,\n                                        embedded_properties,\n                                        property.target_id.unwrap(),\n                                    )\n                                })\n                                .collect();\n                            let list = list?;\n                            let objects = list\n                                .iter()\n                                .map(|o| o.as_ref().map(|o| o.finish()))\n                                .collect_vec();\n                            ob.write_object_list(property.offset, Some(objects.as_slice()));\n                        } else {\n                            return Err(IsarError::InvalidJson {});\n                        }\n                    }\n                }\n            } else {\n                ob.write_null(property.offset, property.data_type);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn value_to_bool(value: &Value) -> Result<Option<bool>> {\n        if value.is_null() {\n            return Ok(None);\n        } else if let Some(value) = value.as_bool() {\n            return Ok(Some(value));\n        };\n        Err(IsarError::InvalidJson {})\n    }\n\n    fn value_to_byte(value: &Value) -> Result<u8> {\n        if value.is_null() {\n            return Ok(IsarObject::NULL_BYTE);\n        } else if let Some(value) = value.as_i64() {\n            if value >= 0 && value <= u8::MAX as i64 {\n                return Ok(value as u8);\n            }\n        }\n        Err(IsarError::InvalidJson {})\n    }\n\n    fn value_to_int(value: &Value) -> Result<i32> {\n        if value.is_null() {\n            return Ok(IsarObject::NULL_INT);\n        } else if let Some(value) = value.as_i64() {\n            if value >= i32::MIN as i64 && value <= i32::MAX as i64 {\n                return Ok(value as i32);\n            }\n        }\n        Err(IsarError::InvalidJson {})\n    }\n\n    fn value_to_float(value: &Value) -> Result<f32> {\n        if value.is_null() {\n            return Ok(IsarObject::NULL_FLOAT);\n        } else if let Some(value) = value.as_f64() {\n            if value >= f32::MIN as f64 && value <= f32::MAX as f64 {\n                return Ok(value as f32);\n            }\n        }\n        Err(IsarError::InvalidJson {})\n    }\n\n    fn value_to_long(value: &Value) -> Result<i64> {\n        if value.is_null() {\n            Ok(IsarObject::NULL_LONG)\n        } else if let Some(value) = value.as_i64() {\n            Ok(value)\n        } else {\n            Err(IsarError::InvalidJson {})\n        }\n    }\n\n    fn value_to_double(value: &Value) -> Result<f64> {\n        if value.is_null() {\n            Ok(IsarObject::NULL_DOUBLE)\n        } else if let Some(value) = value.as_f64() {\n            Ok(value)\n        } else {\n            Err(IsarError::InvalidJson {})\n        }\n    }\n\n    fn value_to_string(value: &Value) -> Result<Option<&str>> {\n        if value.is_null() {\n            Ok(None)\n        } else if let Some(value) = value.as_str() {\n            Ok(Some(value))\n        } else {\n            Err(IsarError::InvalidJson {})\n        }\n    }\n\n    fn value_to_object(\n        value: &Value,\n        embedded_properties: &IntMap<Vec<Property>>,\n        target_id: u64,\n    ) -> Result<Option<ObjectBuilder>> {\n        if value.is_null() {\n            Ok(None)\n        } else {\n            let properties = embedded_properties.get(target_id).unwrap();\n            let mut embedded_ob = ObjectBuilder::new(properties, None);\n            Self::decode(properties, embedded_properties, &mut embedded_ob, value)?;\n            Ok(Some(embedded_ob))\n        }\n    }\n\n    fn value_to_array<T, F>(value: &Value, convert: F) -> Result<Option<Vec<T>>>\n    where\n        F: Fn(&Value) -> Result<T>,\n    {\n        if value.is_null() {\n            Ok(None)\n        } else if let Some(value) = value.as_array() {\n            let array: Result<Vec<T>> = value.iter().map(convert).collect();\n            Ok(Some(array?))\n        } else {\n            Err(IsarError::InvalidJson {})\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/object/mod.rs",
    "content": "pub mod data_type;\npub mod id;\npub mod isar_object;\npub mod json_encode_decode;\npub mod object_builder;\npub mod property;\n"
  },
  {
    "path": "packages/isar_core/src/object/object_builder.rs",
    "content": "use byteorder::{ByteOrder, LittleEndian};\nuse itertools::Itertools;\n\nuse crate::object::data_type::DataType;\nuse crate::object::isar_object::IsarObject;\nuse std::slice::from_raw_parts;\n\nuse super::property::Property;\n\n/*\nu16 static properties size\n\n--- Static properties ---\ni64 field1\nu24 field2_offset or 0\nf32 field3\nu24 field4_offset or 0\n\n--- Dynamic data ---\nfield2_offset:\nu24 field2_length\n... field2_data ...\n\nfield4_offset:\nu24 field4_length\nu24 field4_item1_length + 1 or 0\nu24 field4_item2_length + 1 or 0\n... field4_item1 ...\n... field4_item2 ...\n\n*/\n\npub struct ObjectBuilder {\n    buffer: Vec<u8>,\n    dynamic_offset: usize,\n}\n\nimpl ObjectBuilder {\n    pub fn new(properties: &[Property], buffer: Option<Vec<u8>>) -> ObjectBuilder {\n        let static_size = properties\n            .iter()\n            .max_by_key(|p| p.offset)\n            .map_or(0, |p| p.offset + p.data_type.get_static_size());\n\n        let mut buffer = buffer.unwrap_or_else(|| Vec::with_capacity(2 + static_size * 2));\n        buffer.clear();\n\n        let mut ob = ObjectBuilder {\n            buffer,\n            dynamic_offset: static_size,\n        };\n        ob.write_at(0, &(static_size as u16).to_le_bytes());\n        ob\n    }\n\n    #[inline]\n    fn write_at(&mut self, offset: usize, bytes: &[u8]) {\n        if offset + bytes.len() > self.buffer.len() {\n            self.buffer.resize(offset + bytes.len(), 0);\n        }\n        self.buffer[offset..offset + bytes.len()].copy_from_slice(bytes);\n    }\n\n    #[inline]\n    fn write_u24(&mut self, offset: usize, value: usize) {\n        if offset + 3 > self.buffer.len() {\n            self.buffer.resize(offset + 3, 0);\n        }\n        LittleEndian::write_u24(&mut self.buffer[offset..], value as u32);\n    }\n\n    pub fn write_null(&mut self, offset: usize, data_type: DataType) {\n        match data_type {\n            DataType::Bool => self.write_bool(offset, None),\n            DataType::Byte => self.write_byte(offset, IsarObject::NULL_BYTE),\n            DataType::Int => self.write_int(offset, IsarObject::NULL_INT),\n            DataType::Float => self.write_float(offset, IsarObject::NULL_FLOAT),\n            DataType::Long => self.write_long(offset, IsarObject::NULL_LONG),\n            DataType::Double => self.write_double(offset, IsarObject::NULL_DOUBLE),\n            DataType::String => self.write_string(offset, None),\n            DataType::Object => self.write_object(offset, None),\n            DataType::BoolList => self.write_bool_list(offset, None),\n            DataType::ByteList => self.write_byte_list(offset, None),\n            DataType::IntList => self.write_int_list(offset, None),\n            DataType::FloatList => self.write_float_list(offset, None),\n            DataType::LongList => self.write_long_list(offset, None),\n            DataType::DoubleList => self.write_double_list(offset, None),\n            DataType::StringList => self.write_string_list(offset, None),\n            DataType::ObjectList => self.write_object_list(offset, None),\n        }\n    }\n\n    pub fn bool_to_byte(value: Option<bool>) -> u8 {\n        if let Some(value) = value {\n            if value {\n                IsarObject::TRUE_BOOL\n            } else {\n                IsarObject::FALSE_BOOL\n            }\n        } else {\n            IsarObject::NULL_BOOL\n        }\n    }\n\n    pub fn write_byte(&mut self, offset: usize, value: u8) {\n        self.write_at(offset, &[value]);\n    }\n\n    pub fn write_bool(&mut self, offset: usize, value: Option<bool>) {\n        let value = Self::bool_to_byte(value);\n        self.write_at(offset, &[value]);\n    }\n\n    pub fn write_int(&mut self, offset: usize, value: i32) {\n        self.write_at(offset, &value.to_le_bytes());\n    }\n\n    pub fn write_float(&mut self, offset: usize, value: f32) {\n        self.write_at(offset, &value.to_le_bytes());\n    }\n\n    pub fn write_long(&mut self, offset: usize, value: i64) {\n        self.write_at(offset, &value.to_le_bytes());\n    }\n\n    pub fn write_double(&mut self, offset: usize, value: f64) {\n        self.write_at(offset, &value.to_le_bytes());\n    }\n\n    pub fn write_string(&mut self, offset: usize, value: Option<&str>) {\n        let bytes = value.map(|s| s.as_ref());\n        self.write_list(offset, bytes);\n    }\n\n    pub fn write_object(&mut self, offset: usize, value: Option<IsarObject>) {\n        self.write_list(offset, value.as_ref().map(|o| o.as_bytes()));\n    }\n\n    pub fn write_bool_list(&mut self, offset: usize, value: Option<&[Option<bool>]>) {\n        let list = value.map(|list| list.iter().map(|b| Self::bool_to_byte(*b)).collect_vec());\n        self.write_list(offset, list.as_deref());\n    }\n\n    pub fn write_byte_list(&mut self, offset: usize, value: Option<&[u8]>) {\n        self.write_list(offset, value);\n    }\n\n    pub fn write_int_list(&mut self, offset: usize, value: Option<&[i32]>) {\n        self.write_list(offset, value);\n    }\n\n    pub fn write_float_list(&mut self, offset: usize, value: Option<&[f32]>) {\n        self.write_list(offset, value);\n    }\n\n    pub fn write_long_list(&mut self, offset: usize, value: Option<&[i64]>) {\n        self.write_list(offset, value);\n    }\n\n    pub fn write_double_list(&mut self, offset: usize, value: Option<&[f64]>) {\n        self.write_list(offset, value);\n    }\n\n    pub fn write_string_list(&mut self, offset: usize, value: Option<&[Option<&str>]>) {\n        self.write_list_list(offset, value, |v| v.as_bytes())\n    }\n\n    pub fn write_object_list(&mut self, offset: usize, value: Option<&[Option<IsarObject>]>) {\n        self.write_list_list(offset, value, |v| v.as_bytes())\n    }\n\n    fn write_list<T>(&mut self, offset: usize, list: Option<&[T]>) {\n        if let Some(list) = list {\n            let bytes = Self::get_list_bytes(list);\n            self.write_u24(offset, self.dynamic_offset);\n            self.write_u24(self.dynamic_offset, list.len());\n            self.write_at(self.dynamic_offset + 3, bytes);\n            self.dynamic_offset += bytes.len() + 3;\n        } else {\n            self.write_u24(offset, 0);\n        }\n    }\n\n    fn write_list_list<T>(\n        &mut self,\n        offset: usize,\n        value: Option<&[Option<T>]>,\n        to_bytes: impl Fn(&T) -> &[u8],\n    ) {\n        if let Some(value) = value {\n            self.write_u24(offset, self.dynamic_offset);\n            self.write_u24(self.dynamic_offset, value.len());\n\n            let mut offset_list_offset = self.dynamic_offset + 3;\n            self.dynamic_offset += 3 + value.len() * 3;\n            for v in value {\n                if let Some(bytes) = v.as_ref().map(|v| to_bytes(v)) {\n                    self.write_u24(offset_list_offset, bytes.len() + 1);\n                    self.write_at(self.dynamic_offset, bytes);\n                    self.dynamic_offset += bytes.len();\n                } else {\n                    self.write_u24(offset_list_offset, 0);\n                }\n                offset_list_offset += 3;\n            }\n        } else {\n            self.write_u24(offset, 0);\n        }\n    }\n\n    #[inline]\n    pub(crate) fn get_list_bytes<T>(list: &[T]) -> &[u8] {\n        let type_size = std::mem::size_of::<T>();\n        let ptr = list.as_ptr() as *const T;\n        unsafe { from_raw_parts::<u8>(ptr as *const u8, list.len() * type_size) }\n    }\n\n    pub fn finish(&self) -> IsarObject {\n        IsarObject::from_bytes(&self.buffer)\n    }\n\n    pub fn recycle(self) -> Vec<u8> {\n        let mut buffer = self.buffer;\n        buffer.clear();\n        buffer\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::ObjectBuilder;\n    use crate::object::data_type::DataType::{self, *};\n    use crate::object::isar_object::IsarObject;\n    use crate::object::property::Property;\n\n    macro_rules! builder {\n        ($var:ident, $prop:ident, $type:ident) => {\n            let $prop = Property::debug($type, 3);\n            let props = vec![Property::debug(Byte, 2), $prop.clone()];\n            let mut $var = ObjectBuilder::new(&props, None);\n            $var.write_byte(2, 255);\n        };\n    }\n\n    fn offset_size(value: usize) -> [u8; 3] {\n        let mut bytes = [0; 3];\n        bytes[2] = (value >> 16) as u8;\n        bytes[1] = (value >> 8) as u8;\n        bytes[0] = value as u8;\n        bytes\n    }\n\n    #[test]\n    pub fn test_write_null() {\n        builder!(b, p, Bool);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, 0]);\n\n        builder!(b, p, Byte);\n        b.write_null(p.offset, p.data_type);\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, 0]);\n\n        builder!(b, p, Int);\n        b.write_null(p.offset, p.data_type);\n        let mut bytes = vec![7, 0, 255];\n        bytes.extend_from_slice(&IsarObject::NULL_INT.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, Float);\n        b.write_null(p.offset, p.data_type);\n        let mut bytes = vec![7, 0, 255];\n        bytes.extend_from_slice(&IsarObject::NULL_FLOAT.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, Long);\n        b.write_null(p.offset, p.data_type);\n        let mut bytes = vec![11, 0, 255];\n        bytes.extend_from_slice(&IsarObject::NULL_LONG.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, Double);\n        b.write_null(p.offset, p.data_type);\n        let mut bytes = vec![11, 0, 255];\n        bytes.extend_from_slice(&IsarObject::NULL_DOUBLE.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        let list_types = vec![\n            String, Object, ByteList, IntList, FloatList, LongList, DoubleList, StringList,\n            ObjectList,\n        ];\n\n        for list_type in list_types {\n            builder!(b, p, list_type);\n            b.write_null(p.offset, p.data_type);\n            let bytes = vec![6, 0, 255, 0, 0, 0];\n            assert_eq!(b.finish().as_bytes(), &bytes);\n        }\n    }\n\n    #[test]\n    pub fn test_write_bool() {\n        builder!(b, p, Bool);\n        b.write_bool(p.offset, Some(true));\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, IsarObject::TRUE_BOOL]);\n\n        builder!(b, p, Bool);\n        b.write_bool(p.offset, Some(false));\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, IsarObject::FALSE_BOOL]);\n\n        builder!(b, p, Bool);\n        b.write_bool(p.offset, None);\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, IsarObject::NULL_BOOL]);\n    }\n\n    #[test]\n    pub fn test_write_byte() {\n        builder!(b, p, Byte);\n        b.write_byte(p.offset, 0);\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, 0]);\n\n        builder!(b, p, Byte);\n        b.write_byte(p.offset, 123);\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, 123]);\n\n        builder!(b, p, Byte);\n        b.write_byte(p.offset, 255);\n        assert_eq!(b.finish().as_bytes(), &[4, 0, 255, 255]);\n    }\n\n    #[test]\n    pub fn test_write_int() {\n        builder!(b, p, Int);\n        b.write_int(p.offset, 123);\n        assert_eq!(b.finish().as_bytes(), &[7, 0, 255, 123, 0, 0, 0])\n    }\n\n    #[test]\n    pub fn test_write_float() {\n        builder!(b, p, Float);\n        b.write_float(p.offset, 123.123);\n        let mut bytes = vec![7, 0, 255];\n        bytes.extend_from_slice(&123.123f32.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, Float);\n        b.write_float(p.offset, f32::NAN);\n        let mut bytes = vec![7, 0, 255];\n        bytes.extend_from_slice(&f32::NAN.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_long() {\n        builder!(b, p, Long);\n        b.write_long(p.offset, 123123);\n        let mut bytes = vec![11, 0, 255];\n        bytes.extend_from_slice(&123123i64.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes)\n    }\n\n    #[test]\n    pub fn test_write_double() {\n        builder!(b, p, Double);\n        b.write_double(p.offset, 123.123);\n        let mut bytes = vec![11, 0, 255];\n        bytes.extend_from_slice(&123.123f64.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, Double);\n        b.write_double(p.offset, f64::NAN);\n        let mut bytes = vec![11, 0, 255];\n        bytes.extend_from_slice(&f64::NAN.to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_string() {\n        builder!(b, p, String);\n        b.write_string(p.offset, Some(\"hello\"));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(5));\n        bytes.extend_from_slice(b\"hello\");\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, String);\n        b.write_string(p.offset, Some(\"\"));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, String);\n        b.write_string(p.offset, None);\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_object() {\n        builder!(b, p, Object);\n        let object = IsarObject::from_bytes(&[3, 0, 111]);\n        b.write_object(p.offset, Some(object));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(3));\n        bytes.extend_from_slice(&[3, 0, 111]);\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_multiple_static_types() {\n        let props = vec![\n            Property::debug(DataType::Long, 2),\n            Property::debug(DataType::Byte, 10),\n            Property::debug(DataType::Int, 11),\n            Property::debug(DataType::Float, 15),\n            Property::debug(DataType::Long, 19),\n            Property::debug(DataType::Double, 27),\n        ];\n        let mut b = ObjectBuilder::new(&props, None);\n\n        b.write_long(props.get(0).unwrap().offset, 1);\n        b.write_byte(props.get(1).unwrap().offset, u8::MAX);\n        b.write_int(props.get(2).unwrap().offset, i32::MAX);\n        b.write_float(props.get(3).unwrap().offset, std::f32::consts::E);\n        b.write_long(props.get(4).unwrap().offset, i64::MIN);\n        b.write_double(props.get(5).unwrap().offset, std::f64::consts::PI);\n\n        let mut bytes = vec![35, 0, 1, 0, 0, 0, 0, 0, 0, 0];\n        bytes.push(u8::MAX);\n        bytes.extend_from_slice(&i32::MAX.to_le_bytes());\n        bytes.extend_from_slice(&std::f32::consts::E.to_le_bytes());\n        bytes.extend_from_slice(&i64::MIN.to_le_bytes());\n        bytes.extend_from_slice(&std::f64::consts::PI.to_le_bytes());\n\n        assert_eq!(b.finish().as_bytes(), bytes);\n    }\n\n    #[test]\n    pub fn test_write_byte_list() {\n        builder!(b, p, ByteList);\n        b.write_byte_list(p.offset, Some(&[1, 2, 3]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(3));\n        bytes.extend_from_slice(&[1, 2, 3]);\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, ByteList);\n        b.write_byte_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_int_list() {\n        builder!(b, p, IntList);\n        b.write_int_list(p.offset, Some(&[1, -10]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(2));\n        bytes.extend_from_slice(&1i32.to_le_bytes());\n        bytes.extend_from_slice(&(-10i32).to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, IntList);\n        b.write_int_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_float_list() {\n        builder!(b, p, FloatList);\n        b.write_float_list(p.offset, Some(&[1.1, -10.10]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(2));\n        bytes.extend_from_slice(&1.1f32.to_le_bytes());\n        bytes.extend_from_slice(&(-10.10f32).to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, FloatList);\n        b.write_float_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_long_list() {\n        builder!(b, p, LongList);\n        b.write_long_list(p.offset, Some(&[1, -10]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(2));\n        bytes.extend_from_slice(&1i64.to_le_bytes());\n        bytes.extend_from_slice(&(-10i64).to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, LongList);\n        b.write_long_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_double_list() {\n        builder!(b, p, DoubleList);\n        b.write_double_list(p.offset, Some(&[1.1, -10.10]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(2));\n        bytes.extend_from_slice(&1.1f64.to_le_bytes());\n        bytes.extend_from_slice(&(-10.10f64).to_le_bytes());\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, DoubleList);\n        b.write_double_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_string_list() {\n        builder!(b, p, StringList);\n        b.write_string_list(p.offset, Some(&[Some(\"abc\"), None, Some(\"\"), Some(\"de\")]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(4));\n        bytes.extend_from_slice(&offset_size(4));\n        bytes.extend_from_slice(&offset_size(0));\n        bytes.extend_from_slice(&offset_size(1));\n        bytes.extend_from_slice(&offset_size(3));\n        bytes.extend_from_slice(b\"abcde\");\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, StringList);\n        b.write_string_list(p.offset, Some(&[None]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(1));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, StringList);\n        b.write_string_list(p.offset, Some(&[Some(\"\")]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(1));\n        bytes.extend_from_slice(&offset_size(1));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, StringList);\n        b.write_string_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n\n    #[test]\n    pub fn test_write_object_list() {\n        builder!(b, p, ObjectList);\n        let object1 = IsarObject::from_bytes(&[2, 0]);\n        let object2 = IsarObject::from_bytes(&[3, 0, 123]);\n        b.write_object_list(p.offset, Some(&[Some(object1), None, Some(object2)]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(3));\n        bytes.extend_from_slice(&offset_size(3));\n        bytes.extend_from_slice(&offset_size(0));\n        bytes.extend_from_slice(&offset_size(4));\n        bytes.extend_from_slice(&[2, 0, 3, 0, 123]);\n        assert_eq!(b.finish().as_bytes(), &bytes);\n\n        builder!(b, p, ObjectList);\n        b.write_object_list(p.offset, Some(&[]));\n        let mut bytes = vec![6, 0, 255];\n        bytes.extend_from_slice(&offset_size(6));\n        bytes.extend_from_slice(&offset_size(0));\n        assert_eq!(b.finish().as_bytes(), &bytes);\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/object/property.rs",
    "content": "use xxhash_rust::xxh3::xxh3_64;\n\nuse super::data_type::DataType;\n\n#[derive(Clone, Eq, PartialEq)]\npub struct Property {\n    pub name: String,\n    pub data_type: DataType,\n    pub offset: usize,\n    pub target_id: Option<u64>,\n}\n\nimpl Property {\n    pub fn new(name: &str, data_type: DataType, offset: usize, target_id: Option<&str>) -> Self {\n        let target_id = target_id.map(|col| xxh3_64(col.as_bytes()));\n        Property {\n            name: name.to_string(),\n            data_type,\n            offset,\n            target_id,\n        }\n    }\n\n    pub const fn debug(data_type: DataType, offset: usize) -> Self {\n        Property {\n            name: String::new(),\n            data_type,\n            offset,\n            target_id: None,\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/fast_wild_match.rs",
    "content": "const ASTERISK: u8 = 42;\nconst QUESTION_MARK: u8 = 63;\n\npub(crate) fn fast_wild_match(tame: &str, wild: &str) -> bool {\n    let wild = wild.as_bytes();\n    let tame = tame.as_bytes();\n    let mut i_wild = 0;\n    let mut i_tame = 0;\n    let mut i_last = 0;\n    let mut i_star = 0;\n\n    while tame.get(i_tame).is_some() {\n        match wild.get(i_wild) {\n            Some(&QUESTION_MARK) => {\n                i_tame += 1;\n                i_wild += 1;\n                continue;\n            }\n            Some(&ASTERISK) => {\n                loop {\n                    i_wild += 1;\n                    if wild.get(i_wild) != Some(&ASTERISK) {\n                        break;\n                    }\n                }\n                if wild.get(i_wild).is_none() {\n                    return true;\n                }\n                i_star = i_wild;\n            }\n            _ => {\n                if tame.get(i_tame) == wild.get(i_wild) {\n                    i_tame += 1;\n                    i_wild += 1;\n                    continue;\n                }\n                if i_star == 0 {\n                    return false;\n                }\n                i_wild = i_star;\n                i_tame = i_last + 1;\n            }\n        }\n\n        while tame.get(i_tame) != wild.get(i_wild) && wild.get(i_wild) != Some(&QUESTION_MARK) {\n            i_tame += 1;\n            if tame.get(i_tame).is_none() {\n                return false;\n            }\n        }\n        i_last = i_tame;\n        i_tame += 1;\n        i_wild += 1;\n    }\n    while wild.get(i_wild) == Some(&ASTERISK) {\n        i_wild += 1;\n    }\n    wild.get(i_wild).is_none()\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::query::fast_wild_match::fast_wild_match;\n\n    #[test]\n    fn test_wild() {\n        let wild_cases = vec![\n            (\"Hi\", \"Hi*\", true),\n            (\"abc\", \"ab*d\", false),\n            (\"abcccd\", \"*ccd\", true),\n            (\"mississipissippi\", \"*issip*ss*\", true),\n            (\"xxxx*zzzzzzzzy*f\", \"xxxx*zzy*fffff\", false),\n            (\"xxxx*zzzzzzzzy*f\", \"xxx*zzy*f\", true),\n            (\"xxxxzzzzzzzzyf\", \"xxxx*zzy*fffff\", false),\n            (\"xxxxzzzzzzzzyf\", \"xxxx*zzy*f\", true),\n            (\"xyxyxyzyxyz\", \"xy*z*xyz\", true),\n            (\"mississippi\", \"*sip*\", true),\n            (\"xyxyxyxyz\", \"xy*xyz\", true),\n            (\"mississippi\", \"mi*sip*\", true),\n            (\"ababac\", \"*abac*\", true),\n            (\"ababac\", \"*abac*\", true),\n            (\"aaazz\", \"a*zz*\", true),\n            (\"a12b12\", \"*12*23\", false),\n            (\"a12b12\", \"a12b\", false),\n            (\"a12b12\", \"*12*12*\", true),\n            (\"caaab\", \"*a?b\", true),\n            (\"*\", \"*\", true),\n            (\"a*abab\", \"a*b\", true),\n            (\"a*r\", \"a*\", true),\n            (\"a*ar\", \"a*aar\", false),\n            (\"XYXYXYZYXYz\", \"XY*Z*XYz\", true),\n            (\"missisSIPpi\", \"*SIP*\", true),\n            (\"mississipPI\", \"*issip*PI\", true),\n            (\"xyxyxyxyz\", \"xy*xyz\", true),\n            (\"miSsissippi\", \"mi*sip*\", true),\n            (\"miSsissippi\", \"mi*Sip*\", false),\n            (\"abAbac\", \"*Abac*\", true),\n            (\"abAbac\", \"*Abac*\", true),\n            (\"aAazz\", \"a*zz*\", true),\n            (\"A12b12\", \"*12*23\", false),\n            (\"a12B12\", \"*12*12*\", true),\n            (\"oWn\", \"*oWn*\", true),\n            (\"bLah\", \"bLah\", true),\n            (\"bLah\", \"bLaH\", false),\n            (\"a\", \"*?\", true),\n            (\"ab\", \"*?\", true),\n            (\"abc\", \"*?\", true),\n            (\"a\", \"??\", false),\n            (\"ab\", \"?*?\", true),\n            (\"ab\", \"*?*?*\", true),\n            (\"abc\", \"?**?*?\", true),\n            (\"abc\", \"?**?*&?\", false),\n            (\"abcd\", \"?b*??\", true),\n            (\"abcd\", \"?a*??\", false),\n            (\"abcd\", \"?**?c?\", true),\n            (\"abcd\", \"?**?d?\", false),\n            (\"abcde\", \"?*b*?*d*?\", true),\n            (\"bLah\", \"bL?h\", true),\n            (\"bLaaa\", \"bLa?\", false),\n            (\"bLah\", \"bLa?\", true),\n            (\"bLaH\", \"?Lah\", false),\n            (\"bLaH\", \"?LaH\", true),\n            (\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab\", \"a*a*a*a*a*a*aa*aaa*a*a*b\", true),\n            (\"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\", \"*a*b*ba*ca*a*aa*aaa*fa*ga*b*\", true),\n            (\"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\", \"*a*b*ba*ca*a*x*aaa*fa*ga*b*\", false),\n            (\"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\", \"*a*b*ba*ca*aaaa*fa*ga*gggg*b*\", false),\n            (\"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\", \"*a*b*ba*ca*aaaa*fa*ga*ggg*b*\", true),\n            (\"aaabbaabbaab\", \"*aabbaa*a*\", true),\n            (\"a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*\", \"a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*\", true),\n            (\"aaaaaaaaaaaaaaaaa\", \"*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*\", true),\n            (\"aaaaaaaaaaaaaaaa\", \"*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*\", false),\n            (\"abc*abcd*abcde*abcdef*abcdefg*abcdefgh*abcdefghi*abcdefghij*abcdefghijk*abcdefghijkl*abcdefghijklm*abcdefghijklmn\", \"abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*\", false),\n            (\"abc*abcd*abcde*abcdef*abcdefg*abcdefgh*abcdefghi*abcdefghij*abcdefghijk*abcdefghijkl*abcdefghijklm*abcdefghijklmn\", \"abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*\", true),\n            (\"abc*abcd*abcd*abc*abcd\", \"abc*abc*abc*abc*abc\", false),\n            (\"abc*abcd*abcd*abc*abcd*abcd*abc*abcd*abc*abc*abcd\", \"abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abcd\", true),\n            (\"abc\", \"********a********b********c********\", true),\n            (\"********a********b********c********\", \"abc\", false),\n            (\"abc\", \"********a********b********b********\", false),\n            (\"*abc*\", \"***a*b*c***\", true),\n            (\"\", \"?\", false),\n            (\"\", \"*?\", false),\n            (\"\", \"\", true),\n            (\"\", \"*\", true),\n            (\"a\", \"\", false),\n        ];\n\n        for (tame, wild, result) in wild_cases {\n            assert_eq!(fast_wild_match(tame, wild), result);\n        }\n    }\n\n    #[test]\n    fn test_tame() {\n        let tame_cases = vec![\n            (\"abc\", \"abd\", false),\n            (\"abcccd\", \"abcccd\", true),\n            (\"mississipissippi\", \"mississipissippi\", true),\n            (\"xxxxzzzzzzzzyf\", \"xxxxzzzzzzzzyfffff\", false),\n            (\"xxxxzzzzzzzzyf\", \"xxxxzzzzzzzzyf\", true),\n            (\"xxxxzzzzzzzzyf\", \"xxxxzzy.fffff\", false),\n            (\"xxxxzzzzzzzzyf\", \"xxxxzzzzzzzzyf\", true),\n            (\"xyxyxyzyxyz\", \"xyxyxyzyxyz\", true),\n            (\"mississippi\", \"mississippi\", true),\n            (\"xyxyxyxyz\", \"xyxyxyxyz\", true),\n            (\"m ississippi\", \"m ississippi\", true),\n            (\"ababac\", \"ababac?\", false),\n            (\"dababac\", \"ababac\", false),\n            (\"aaazz\", \"aaazz\", true),\n            (\"a12b12\", \"1212\", false),\n            (\"a12b12\", \"a12b\", false),\n            (\"a12b12\", \"a12b12\", true),\n            (\"n\", \"n\", true),\n            (\"aabab\", \"aabab\", true),\n            (\"ar\", \"ar\", true),\n            (\"aar\", \"aaar\", false),\n            (\"XYXYXYZYXYz\", \"XYXYXYZYXYz\", true),\n            (\"missisSIPpi\", \"missisSIPpi\", true),\n            (\"mississipPI\", \"mississipPI\", true),\n            (\"xyxyxyxyz\", \"xyxyxyxyz\", true),\n            (\"miSsissippi\", \"miSsissippi\", true),\n            (\"miSsissippi\", \"miSsisSippi\", false),\n            (\"abAbac\", \"abAbac\", true),\n            (\"abAbac\", \"abAbac\", true),\n            (\"aAazz\", \"aAazz\", true),\n            (\"A12b12\", \"A12b123\", false),\n            (\"a12B12\", \"a12B12\", true),\n            (\"oWn\", \"oWn\", true),\n            (\"bLah\", \"bLah\", true),\n            (\"bLah\", \"bLaH\", false),\n            (\"a\", \"a\", true),\n            (\"ab\", \"a?\", true),\n            (\"abc\", \"ab?\", true),\n            (\"a\", \"??\", false),\n            (\"ab\", \"??\", true),\n            (\"abc\", \"???\", true),\n            (\"abcd\", \"????\", true),\n            (\"abc\", \"????\", false),\n            (\"abcd\", \"?b??\", true),\n            (\"abcd\", \"?a??\", false),\n            (\"abcd\", \"??c?\", true),\n            (\"abcd\", \"??d?\", false),\n            (\"abcde\", \"?b?d*?\", true),\n            (\n                \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab\",\n                \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab\",\n                true,\n            ),\n            (\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                true,\n            ),\n            (\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajaxalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                false,\n            ),\n            (\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaggggagaaaaaaaab\",\n                false,\n            ),\n            (\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                \"abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab\",\n                true,\n            ),\n            (\"aaabbaabbaab\", \"aaabbaabbaab\", true),\n            (\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", true,\n            ),\n            (\"aaaaaaaaaaaaaaaaa\", \"aaaaaaaaaaaaaaaaa\", true),\n            (\"aaaaaaaaaaaaaaaa\", \"aaaaaaaaaaaaaaaaa\", false),\n            (\n                \"abcabcdabcdeabcdefabcdefgabcdefghabcdefghiabcdefghijabcdefghijkabcdefghijklabcdefghijklmabcdefghijklmn\",\n                \"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc\",\n                false,\n            ),\n            (\n                \"abcabcdabcdeabcdefabcdefgabcdefghabcdefghiabcdefghijabcdefghijkabcdefghijklabcdefghijklmabcdefghijklmn\",\n                \"abcabcdabcdeabcdefabcdefgabcdefghabcdefghiabcdefghijabcdefghijkabcdefghijklabcdefghijklmabcdefghijklmn\",\n                true,\n            ),\n            (\"abcabcdabcdabcabcd\", \"abcabc?abcabcabc\", false),\n            (\n                \"abcabcdabcdabcabcdabcdabcabcdabcabcabcd\",\n                \"abcabc?abc?abcabc?abc?abc?bc?abc?bc?bcd\",\n                true,\n            ),\n            (\"?abc?\", \"?abc?\", true),\n        ];\n\n        for (tame, wild, result) in tame_cases {\n            assert_eq!(fast_wild_match(tame, wild), result);\n        }\n    }\n\n    #[test]\n    fn test_empty() {\n        let empty_cases = vec![\n            (\"\", \"abd\", false),\n            (\"\", \"abcccd\", false),\n            (\"\", \"mississipissippi\", false),\n            (\"\", \"xxxxzzzzzzzzyfffff\", false),\n            (\"\", \"xxxxzzzzzzzzyf\", false),\n            (\"\", \"xxxxzzy.fffff\", false),\n            (\"\", \"xxxxzzzzzzzzyf\", false),\n            (\"\", \"xyxyxyzyxyz\", false),\n            (\"\", \"mississippi\", false),\n            (\"\", \"xyxyxyxyz\", false),\n            (\"\", \"m ississippi\", false),\n            (\"\", \"ababac*\", false),\n            (\"\", \"ababac\", false),\n            (\"\", \"aaazz\", false),\n            (\"\", \"1212\", false),\n            (\"\", \"a12b\", false),\n            (\"\", \"a12b12\", false),\n            // A mix of cases\n            (\"\", \"n\", false),\n            (\"\", \"aabab\", false),\n            (\"\", \"ar\", false),\n            (\"\", \"aaar\", false),\n            (\"\", \"XYXYXYZYXYz\", false),\n            (\"\", \"missisSIPpi\", false),\n            (\"\", \"mississipPI\", false),\n            (\"\", \"xyxyxyxyz\", false),\n            (\"\", \"miSsissippi\", false),\n            (\"\", \"miSsisSippi\", false),\n            (\"\", \"abAbac\", false),\n            (\"\", \"abAbac\", false),\n            (\"\", \"aAazz\", false),\n            (\"\", \"A12b123\", false),\n            (\"\", \"a12B12\", false),\n            (\"\", \"oWn\", false),\n            (\"\", \"bLah\", false),\n            (\"\", \"bLaH\", false),\n            (\"\", \"\", true),\n            (\"abc\", \"\", false),\n            (\"abcccd\", \"\", false),\n            (\"mississipissippi\", \"\", false),\n            (\"xxxxzzzzzzzzyf\", \"\", false),\n            (\"xxxxzzzzzzzzyf\", \"\", false),\n            (\"xxxxzzzzzzzzyf\", \"\", false),\n            (\"xxxxzzzzzzzzyf\", \"\", false),\n            (\"xyxyxyzyxyz\", \"\", false),\n            (\"mississippi\", \"\", false),\n            (\"xyxyxyxyz\", \"\", false),\n            (\"m ississippi\", \"\", false),\n            (\"ababac\", \"\", false),\n            (\"dababac\", \"\", false),\n            (\"aaazz\", \"\", false),\n            (\"a12b12\", \"\", false),\n            (\"a12b12\", \"\", false),\n            (\"a12b12\", \"\", false),\n            (\"n\", \"\", false),\n            (\"aabab\", \"\", false),\n            (\"ar\", \"\", false),\n            (\"aar\", \"\", false),\n            (\"XYXYXYZYXYz\", \"\", false),\n            (\"missisSIPpi\", \"\", false),\n            (\"mississipPI\", \"\", false),\n            (\"xyxyxyxyz\", \"\", false),\n            (\"miSsissippi\", \"\", false),\n            (\"miSsissippi\", \"\", false),\n            (\"abAbac\", \"\", false),\n            (\"abAbac\", \"\", false),\n            (\"aAazz\", \"\", false),\n            (\"A12b12\", \"\", false),\n            (\"a12B12\", \"\", false),\n            (\"oWn\", \"\", false),\n            (\"bLah\", \"\", false),\n            (\"bLah\", \"\", false),\n        ];\n\n        for (tame, wild, result) in empty_cases {\n            assert_eq!(fast_wild_match(tame, wild), result);\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/filter.rs",
    "content": "use crate::collection::IsarCollection;\nuse crate::cursor::IsarCursors;\nuse crate::error::{illegal_arg, Result};\nuse crate::link::IsarLink;\nuse crate::object::data_type::DataType;\nuse crate::object::isar_object::IsarObject;\nuse crate::object::property::Property;\nuse crate::query::fast_wild_match::fast_wild_match;\nuse enum_dispatch::enum_dispatch;\nuse itertools::Itertools;\nuse paste::paste;\n\n#[macro_export]\nmacro_rules! primitive_create {\n    ($data_type:ident, $property:expr, $lower:expr, $upper:expr) => {\n        paste! {\n            if $property.data_type == DataType::$data_type || ($property.data_type == DataType::Bool && DataType::$data_type == DataType::Byte) {\n                Ok(Filter(\n                    FilterCond::[<$data_type Between>]([<$data_type BetweenCond>] {\n                        offset: $property.offset,\n                        $lower,\n                        $upper,\n                    })\n                ))\n            } else if $property.data_type == DataType::[<$data_type List>] || ($property.data_type == DataType::BoolList && DataType::[<$data_type List>] == DataType::ByteList) {\n                Ok(Filter(\n                    FilterCond::[<Any $data_type Between>]([<Any $data_type BetweenCond>] {\n                        offset: $property.offset,\n                        $lower,\n                        $upper,\n                    })\n                ))\n            } else {\n                illegal_arg(\"Property does not support this filter.\")\n            }\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! string_filter_create {\n    ($name:ident, $property:expr, $value:expr, $case_sensitive:expr) => {\n        paste! {\n            {\n                let value = if $case_sensitive {\n                    $value.to_string()\n                } else {\n                    $value.to_lowercase()\n                };\n                let filter_cond = if $property.data_type == DataType::String {\n                    Ok(FilterCond::[<String $name>]([<String $name Cond>] {\n                        offset: $property.offset,\n                        value,\n                        $case_sensitive,\n                    }))\n                } else if $property.data_type == DataType::StringList {\n                    Ok(FilterCond::[<AnyString $name>]([<AnyString $name Cond>] {\n                        offset: $property.offset,\n                        value,\n                        $case_sensitive,\n                    }))\n                } else {\n                    illegal_arg(\"Property does not support this filter.\")\n                }?;\n                Ok(Filter(filter_cond))\n            }\n        }\n    };\n}\n\n#[derive(Clone)]\npub struct Filter(FilterCond);\n\nimpl Filter {\n    pub fn id(lower: i64, upper: i64) -> Filter {\n        let filter_cond = FilterCond::IdBetween(IdBetweenCond { lower, upper });\n        Filter(filter_cond)\n    }\n\n    pub fn byte(property: &Property, lower: u8, upper: u8) -> Result<Filter> {\n        primitive_create!(Byte, property, lower, upper)\n    }\n\n    pub fn int(property: &Property, lower: i32, upper: i32) -> Result<Filter> {\n        primitive_create!(Int, property, lower, upper)\n    }\n\n    pub fn long(property: &Property, lower: i64, upper: i64) -> Result<Filter> {\n        primitive_create!(Long, property, lower, upper)\n    }\n\n    pub fn float(property: &Property, lower: f32, upper: f32) -> Result<Filter> {\n        primitive_create!(Float, property, lower, upper)\n    }\n\n    pub fn double(property: &Property, lower: f64, upper: f64) -> Result<Filter> {\n        primitive_create!(Double, property, lower, upper)\n    }\n\n    pub fn string_to_bytes(str: Option<&str>, case_sensitive: bool) -> Option<Vec<u8>> {\n        if case_sensitive {\n            str.map(|s| s.as_bytes().to_vec())\n        } else {\n            str.map(|s| s.to_lowercase().as_bytes().to_vec())\n        }\n    }\n\n    pub fn string(\n        property: &Property,\n        lower: Option<&str>,\n        upper: Option<&str>,\n        case_sensitive: bool,\n    ) -> Result<Filter> {\n        Self::byte_string(\n            property,\n            Self::string_to_bytes(lower, case_sensitive),\n            Self::string_to_bytes(upper, case_sensitive),\n            case_sensitive,\n        )\n    }\n\n    pub fn byte_string(\n        property: &Property,\n        lower: Option<Vec<u8>>,\n        upper: Option<Vec<u8>>,\n        case_sensitive: bool,\n    ) -> Result<Filter> {\n        let filter_cond = if property.data_type == DataType::String {\n            Ok(FilterCond::StringBetween(StringBetweenCond {\n                offset: property.offset,\n                lower,\n                upper,\n                case_sensitive,\n            }))\n        } else if property.data_type == DataType::StringList {\n            Ok(FilterCond::AnyStringBetween(AnyStringBetweenCond {\n                offset: property.offset,\n                lower,\n                upper,\n                case_sensitive,\n            }))\n        } else {\n            illegal_arg(\"Property does not support this filter.\")\n        }?;\n        Ok(Filter(filter_cond))\n    }\n\n    pub fn string_starts_with(\n        property: &Property,\n        value: &str,\n        case_sensitive: bool,\n    ) -> Result<Filter> {\n        string_filter_create!(StartsWith, property, value, case_sensitive)\n    }\n\n    pub fn string_ends_with(\n        property: &Property,\n        value: &str,\n        case_sensitive: bool,\n    ) -> Result<Filter> {\n        string_filter_create!(EndsWith, property, value, case_sensitive)\n    }\n\n    pub fn string_contains(\n        property: &Property,\n        value: &str,\n        case_sensitive: bool,\n    ) -> Result<Filter> {\n        string_filter_create!(Contains, property, value, case_sensitive)\n    }\n\n    pub fn string_matches(\n        property: &Property,\n        value: &str,\n        case_sensitive: bool,\n    ) -> Result<Filter> {\n        string_filter_create!(Matches, property, value, case_sensitive)\n    }\n\n    pub fn list_length(property: &Property, lower: usize, upper: usize) -> Result<Filter> {\n        let filter_cond = if property.data_type.get_element_type().is_some() {\n            Ok(FilterCond::ListLength(ListLengthCond {\n                offset: property.offset,\n                lower,\n                upper,\n            }))\n        } else {\n            illegal_arg(\"Property does not support this filter.\")\n        }?;\n        Ok(Filter(filter_cond))\n    }\n\n    pub fn null(property: &Property) -> Filter {\n        let filter_cond = FilterCond::Null(NullCond {\n            offset: property.offset,\n            data_type: property.data_type,\n        });\n        Filter(filter_cond)\n    }\n\n    pub fn and(filters: Vec<Filter>) -> Filter {\n        let filters = filters.into_iter().map(|f| f.0).collect_vec();\n        let filter_cond = FilterCond::And(AndCond { filters });\n        Filter(filter_cond)\n    }\n\n    pub fn or(filters: Vec<Filter>) -> Filter {\n        let filters = filters.into_iter().map(|f| f.0).collect_vec();\n        let filter_cond = FilterCond::Or(OrCond { filters });\n        Filter(filter_cond)\n    }\n\n    pub fn xor(filters: Vec<Filter>) -> Filter {\n        let filters = filters.into_iter().map(|f| f.0).collect_vec();\n        let filter_cond = FilterCond::Xor(XorCond { filters });\n        Filter(filter_cond)\n    }\n\n    pub fn not(filter: Filter) -> Filter {\n        let filter_cond = FilterCond::Not(NotCond {\n            filter: Box::new(filter.0),\n        });\n        Filter(filter_cond)\n    }\n\n    pub fn stat(value: bool) -> Filter {\n        let filter_cond = FilterCond::Static(StaticCond { value });\n        Filter(filter_cond)\n    }\n\n    pub fn object(property: &Property, filter: Option<Filter>) -> Result<Filter> {\n        let filter_cond = if property.data_type == DataType::Object {\n            if let Some(filter) = filter {\n                Ok(FilterCond::Object(ObjectCond {\n                    offset: property.offset,\n                    filter: Box::new(filter.0),\n                }))\n            } else {\n                Ok(FilterCond::Null(NullCond {\n                    offset: property.offset,\n                    data_type: DataType::Object,\n                }))\n            }\n        } else if property.data_type == DataType::ObjectList {\n            Ok(FilterCond::AnyObject(AnyObjectCond {\n                offset: property.offset,\n                filter: filter.map(|f| Box::new(f.0)),\n            }))\n        } else {\n            illegal_arg(\"Property does not support this filter.\")\n        }?;\n        Ok(Filter(filter_cond))\n    }\n\n    pub fn link(collection: &IsarCollection, link_id: u64, filter: Filter) -> Result<Filter> {\n        let link = collection.get_link_backlink(link_id)?.clone();\n        let filter_cond = FilterCond::AnyLink(AnyLinkCond {\n            link,\n            filter: Box::new(filter.0),\n        });\n        Ok(Filter(filter_cond))\n    }\n\n    pub fn link_length(\n        collection: &IsarCollection,\n        link_id: u64,\n        lower: usize,\n        upper: usize,\n    ) -> Result<Filter> {\n        let link = collection.get_link_backlink(link_id)?.clone();\n        let filter_cond = FilterCond::LinkLength(LinkLengthCond { link, lower, upper });\n        Ok(Filter(filter_cond))\n    }\n\n    pub(crate) fn evaluate(\n        &self,\n        id: i64,\n        object: IsarObject,\n        cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        self.0.evaluate(id, object, cursors)\n    }\n}\n\n#[enum_dispatch]\n#[derive(Clone)]\nenum FilterCond {\n    IdBetween(IdBetweenCond),\n    ByteBetween(ByteBetweenCond),\n    IntBetween(IntBetweenCond),\n    LongBetween(LongBetweenCond),\n    FloatBetween(FloatBetweenCond),\n    DoubleBetween(DoubleBetweenCond),\n\n    StringBetween(StringBetweenCond),\n    StringStartsWith(StringStartsWithCond),\n    StringEndsWith(StringEndsWithCond),\n    StringContains(StringContainsCond),\n    StringMatches(StringMatchesCond),\n\n    AnyByteBetween(AnyByteBetweenCond),\n    AnyIntBetween(AnyIntBetweenCond),\n    AnyLongBetween(AnyLongBetweenCond),\n    AnyFloatBetween(AnyFloatBetweenCond),\n    AnyDoubleBetween(AnyDoubleBetweenCond),\n\n    AnyStringBetween(AnyStringBetweenCond),\n    AnyStringStartsWith(AnyStringStartsWithCond),\n    AnyStringEndsWith(AnyStringEndsWithCond),\n    AnyStringContains(AnyStringContainsCond),\n    AnyStringMatches(AnyStringMatchesCond),\n\n    ListLength(ListLengthCond),\n\n    Null(NullCond),\n    And(AndCond),\n    Or(OrCond),\n    Xor(XorCond),\n    Not(NotCond),\n    Static(StaticCond),\n\n    Object(ObjectCond),\n    AnyObject(AnyObjectCond),\n\n    AnyLink(AnyLinkCond),\n    LinkLength(LinkLengthCond),\n}\n\n#[enum_dispatch(FilterCond)]\ntrait Condition {\n    fn evaluate(&self, id: i64, object: IsarObject, cursors: Option<&IsarCursors>) -> Result<bool>;\n}\n\n#[derive(Clone)]\nstruct IdBetweenCond {\n    lower: i64,\n    upper: i64,\n}\n\nimpl Condition for IdBetweenCond {\n    fn evaluate(&self, id: i64, _object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n        Ok(self.lower <= id && self.upper >= id)\n    }\n}\n\n#[macro_export]\nmacro_rules! filter_between_struct {\n    ($name:ident, $data_type:ident, $type:ty) => {\n        #[derive(Clone)]\n        struct $name {\n            upper: $type,\n            lower: $type,\n            offset: usize,\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! primitive_filter_between {\n    ($name:ident, $prop_accessor:ident) => {\n        impl Condition for $name {\n            fn evaluate(\n                &self,\n                _id: i64,\n                object: IsarObject,\n                _: Option<&IsarCursors>,\n            ) -> Result<bool> {\n                let val = object.$prop_accessor(self.offset);\n                Ok(self.lower <= val && self.upper >= val)\n            }\n        }\n    };\n}\n\nfilter_between_struct!(ByteBetweenCond, Byte, u8);\nprimitive_filter_between!(ByteBetweenCond, read_byte);\nfilter_between_struct!(IntBetweenCond, Int, i32);\nprimitive_filter_between!(IntBetweenCond, read_int);\nfilter_between_struct!(LongBetweenCond, Long, i64);\nprimitive_filter_between!(LongBetweenCond, read_long);\n\n#[macro_export]\nmacro_rules! primitive_filter_between_list {\n    ($name:ident, $prop_accessor:ident) => {\n        impl Condition for $name {\n            fn evaluate(\n                &self,\n                _id: i64,\n                object: IsarObject,\n                _: Option<&IsarCursors>,\n            ) -> Result<bool> {\n                let vals = object.$prop_accessor(self.offset);\n                if let Some(vals) = vals {\n                    for val in vals {\n                        if self.lower <= val && self.upper >= val {\n                            return Ok(true);\n                        }\n                    }\n                }\n                Ok(false)\n            }\n        }\n    };\n}\n\nfilter_between_struct!(AnyByteBetweenCond, Byte, u8);\n\nimpl Condition for AnyByteBetweenCond {\n    fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n        let vals = object.read_byte_list(self.offset);\n        if let Some(vals) = vals {\n            for val in vals {\n                if self.lower <= *val && self.upper >= *val {\n                    return Ok(true);\n                }\n            }\n        }\n        Ok(false)\n    }\n}\n\nfilter_between_struct!(AnyIntBetweenCond, Int, i32);\nprimitive_filter_between_list!(AnyIntBetweenCond, read_int_list);\nfilter_between_struct!(AnyLongBetweenCond, Long, i64);\nprimitive_filter_between_list!(AnyLongBetweenCond, read_long_list);\n\n#[macro_export]\nmacro_rules! float_filter_between {\n    ($name:ident, $prop_accessor:ident) => {\n        impl Condition for $name {\n            fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n                let val = object.$prop_accessor(self.offset);\n                Ok(float_filter_between!(eval val, self.lower, self.upper))\n            }\n        }\n    };\n\n    (eval $val:expr, $lower:expr, $upper:expr) => {{\n        ($lower <= $val || $lower.is_nan()) &&\n        ($upper >= $val || $val.is_nan() || ($upper.is_infinite() && $upper.is_sign_positive()))\n    }};\n}\n\nfilter_between_struct!(FloatBetweenCond, Float, f32);\nfloat_filter_between!(FloatBetweenCond, read_float);\nfilter_between_struct!(DoubleBetweenCond, Double, f64);\nfloat_filter_between!(DoubleBetweenCond, read_double);\n\n#[macro_export]\nmacro_rules! float_filter_between_list {\n    ($name:ident, $prop_accessor:ident) => {\n        impl Condition for $name {\n            fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n                let vals = object.$prop_accessor(self.offset);\n                if let Some(vals) = vals {\n                    for val in vals {\n                        if float_filter_between!(eval val, self.lower, self.upper) {\n                            return Ok(true);\n                        }\n                    }\n                }\n                Ok(false)\n            }\n        }\n    };\n}\n\nfilter_between_struct!(AnyFloatBetweenCond, Float, f32);\nfloat_filter_between_list!(AnyFloatBetweenCond, read_float_list);\nfilter_between_struct!(AnyDoubleBetweenCond, Double, f64);\nfloat_filter_between_list!(AnyDoubleBetweenCond, read_double_list);\n\n#[derive(Clone)]\nstruct StringBetweenCond {\n    offset: usize,\n    lower: Option<Vec<u8>>,\n    upper: Option<Vec<u8>>,\n    case_sensitive: bool,\n}\n\n#[derive(Clone)]\nstruct AnyStringBetweenCond {\n    offset: usize,\n    lower: Option<Vec<u8>>,\n    upper: Option<Vec<u8>>,\n    case_sensitive: bool,\n}\n\nfn string_between(\n    value: Option<&str>,\n    lower: Option<&[u8]>,\n    upper: Option<&[u8]>,\n    case_sensitive: bool,\n) -> bool {\n    if let Some(obj_str) = value {\n        let mut matches = true;\n        if case_sensitive {\n            if let Some(lower) = lower {\n                matches = lower <= obj_str.as_bytes();\n            }\n            matches &= if let Some(upper) = upper {\n                upper >= obj_str.as_bytes()\n            } else {\n                false\n            };\n        } else {\n            let obj_str = obj_str.to_lowercase();\n            if let Some(lower) = lower {\n                matches = lower <= obj_str.as_bytes();\n            }\n            matches &= if let Some(upper) = upper {\n                upper >= obj_str.as_bytes()\n            } else {\n                false\n            };\n        }\n        matches\n    } else {\n        lower.is_none()\n    }\n}\n\nimpl Condition for StringBetweenCond {\n    fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n        let value = object.read_string(self.offset);\n        let result = string_between(\n            value,\n            self.lower.as_deref(),\n            self.upper.as_deref(),\n            self.case_sensitive,\n        );\n        Ok(result)\n    }\n}\n\nimpl Condition for AnyStringBetweenCond {\n    fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n        let list = object.read_string_list(self.offset);\n        if let Some(list) = list {\n            for value in list {\n                let result = string_between(\n                    value,\n                    self.lower.as_deref(),\n                    self.upper.as_deref(),\n                    self.case_sensitive,\n                );\n                if result {\n                    return Ok(true);\n                }\n            }\n        }\n        Ok(false)\n    }\n}\n\n#[macro_export]\nmacro_rules! string_filter_struct {\n    ($name:ident) => {\n        paste! {\n            #[derive(Clone)]\n            struct [<$name Cond>] {\n                offset: usize,\n                value: String,\n                case_sensitive: bool,\n            }\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! string_filter {\n    ($name:ident) => {\n        paste! {\n            string_filter_struct!($name);\n            impl Condition for [<$name Cond>] {\n                fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n                    let other_str = object.read_string(self.offset);\n                    let result = string_filter!(eval $name, self, other_str);\n                    Ok(result)\n                }\n            }\n\n            string_filter_struct!([<Any $name>]);\n            impl Condition for [<Any $name Cond>] {\n                fn evaluate(&self, _id: i64, object: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n                    let list = object.read_string_list(self.offset);\n                    if let Some(list) = list {\n                        for value in list {\n                            if string_filter!(eval $name, self, value) {\n                                return Ok(true);\n                            }\n                        }\n                    }\n                    Ok(false)\n                }\n            }\n        }\n    };\n\n    (eval $name:tt, $filter:expr, $value:expr) => {\n        if let Some(other_str) = $value {\n            if $filter.case_sensitive {\n                string_filter!($name &$filter.value, other_str)\n            } else {\n                let lowercase_string = other_str.to_lowercase();\n                let lowercase_str = &lowercase_string;\n                string_filter!($name &$filter.value, lowercase_str)\n            }\n        } else {\n            false\n        }\n    };\n\n    (StringStartsWith $filter_str:expr, $other_str:ident) => {\n        $other_str.starts_with($filter_str)\n    };\n\n    (StringEndsWith $filter_str:expr, $other_str:ident) => {\n        $other_str.ends_with($filter_str)\n    };\n\n    (StringContains $filter_str:expr, $other_str:ident) => {\n        $other_str.contains($filter_str)\n    };\n\n    (StringMatches $filter_str:expr, $other_str:ident) => {\n        fast_wild_match($other_str, $filter_str)\n    };\n}\n\nstring_filter!(StringStartsWith);\nstring_filter!(StringEndsWith);\nstring_filter!(StringContains);\nstring_filter!(StringMatches);\n\n#[derive(Clone)]\nstruct ListLengthCond {\n    offset: usize,\n    lower: usize,\n    upper: usize,\n}\n\nimpl Condition for ListLengthCond {\n    fn evaluate(\n        &self,\n        _id: i64,\n        object: IsarObject,\n        _cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        if let Some(len) = object.read_length(self.offset) {\n            Ok(self.lower <= len && self.upper >= len)\n        } else {\n            Ok(false)\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct NullCond {\n    offset: usize,\n    data_type: DataType,\n}\n\nimpl Condition for NullCond {\n    fn evaluate(\n        &self,\n        _id: i64,\n        object: IsarObject,\n        _cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        Ok(object.is_null(self.offset, self.data_type))\n    }\n}\n\n#[derive(Clone)]\nstruct AndCond {\n    filters: Vec<FilterCond>,\n}\n\nimpl Condition for AndCond {\n    fn evaluate(&self, id: i64, object: IsarObject, cursors: Option<&IsarCursors>) -> Result<bool> {\n        for filter in &self.filters {\n            if !filter.evaluate(id, object, cursors)? {\n                return Ok(false);\n            }\n        }\n        Ok(true)\n    }\n}\n\n#[derive(Clone)]\nstruct OrCond {\n    filters: Vec<FilterCond>,\n}\n\nimpl Condition for OrCond {\n    fn evaluate(&self, id: i64, object: IsarObject, cursors: Option<&IsarCursors>) -> Result<bool> {\n        for filter in &self.filters {\n            if filter.evaluate(id, object, cursors)? {\n                return Ok(true);\n            }\n        }\n        Ok(false)\n    }\n}\n\n#[derive(Clone)]\nstruct XorCond {\n    filters: Vec<FilterCond>,\n}\n\nimpl Condition for XorCond {\n    fn evaluate(&self, id: i64, object: IsarObject, cursors: Option<&IsarCursors>) -> Result<bool> {\n        let mut any = false;\n        for filter in &self.filters {\n            if filter.evaluate(id, object, cursors)? {\n                if any {\n                    return Ok(false);\n                } else {\n                    any = true;\n                }\n            }\n        }\n        Ok(any)\n    }\n}\n\n#[derive(Clone)]\nstruct NotCond {\n    filter: Box<FilterCond>,\n}\n\nimpl Condition for NotCond {\n    fn evaluate(&self, id: i64, object: IsarObject, cursors: Option<&IsarCursors>) -> Result<bool> {\n        Ok(!self.filter.evaluate(id, object, cursors)?)\n    }\n}\n\n#[derive(Clone)]\nstruct StaticCond {\n    value: bool,\n}\n\nimpl Condition for StaticCond {\n    fn evaluate(&self, _id: i64, _: IsarObject, _: Option<&IsarCursors>) -> Result<bool> {\n        Ok(self.value)\n    }\n}\n\n#[derive(Clone)]\nstruct ObjectCond {\n    offset: usize,\n    filter: Box<FilterCond>,\n}\n\nimpl Condition for ObjectCond {\n    fn evaluate(\n        &self,\n        _id: i64,\n        object: IsarObject,\n        _cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        if let Some(object) = object.read_object(self.offset) {\n            self.filter.evaluate(i64::MIN, object, None)\n        } else {\n            Ok(false)\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct AnyObjectCond {\n    offset: usize,\n    filter: Option<Box<FilterCond>>,\n}\n\nimpl Condition for AnyObjectCond {\n    fn evaluate(\n        &self,\n        _id: i64,\n        object: IsarObject,\n        _cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        if let Some(list) = object.read_object_list(self.offset) {\n            if let Some(filter) = &self.filter {\n                for object in list {\n                    if let Some(object) = object {\n                        let result = filter.evaluate(0, object, None)?;\n                        if result {\n                            return Ok(true);\n                        }\n                    }\n                }\n            } else {\n                for object in list {\n                    if object.is_none() {\n                        return Ok(true);\n                    }\n                }\n            }\n        }\n        Ok(false)\n    }\n}\n\n#[derive(Clone)]\nstruct AnyLinkCond {\n    link: IsarLink,\n    filter: Box<FilterCond>,\n}\n\nimpl Condition for AnyLinkCond {\n    fn evaluate(\n        &self,\n        id: i64,\n        _object: IsarObject,\n        cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        if let Some(cursors) = cursors {\n            self.link\n                .iter(cursors, id, |id, object| {\n                    self.filter\n                        .evaluate(id, object, Some(cursors))\n                        .map(|matches| !matches)\n                })\n                .map(|none_matches| !none_matches)\n        } else {\n            Ok(true)\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct LinkLengthCond {\n    link: IsarLink,\n    lower: usize,\n    upper: usize,\n}\n\nimpl Condition for LinkLengthCond {\n    fn evaluate(\n        &self,\n        id: i64,\n        _object: IsarObject,\n        cursors: Option<&IsarCursors>,\n    ) -> Result<bool> {\n        if let Some(cursors) = cursors {\n            let mut length = 0;\n            self.link.iter_ids(cursors, id, |_, _| {\n                length += 1;\n                Ok(true)\n            })?;\n\n            Ok(self.lower <= length && self.upper >= length)\n        } else {\n            Ok(true)\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/id_where_clause.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::Result;\nuse crate::mdbx::db::Db;\nuse crate::object::id::BytesToId;\nuse crate::object::isar_object::IsarObject;\nuse crate::query::Sort;\nuse intmap::IntMap;\n\n#[derive(Clone)]\npub(crate) struct IdWhereClause {\n    db: Db,\n    lower: i64,\n    upper: i64,\n    sort: Sort,\n}\n\nimpl IdWhereClause {\n    pub(crate) fn new(db: Db, lower: i64, upper: i64, sort: Sort) -> Self {\n        IdWhereClause {\n            db,\n            lower,\n            upper,\n            sort,\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.upper < self.lower\n    }\n\n    pub(crate) fn id_matches(&self, id: i64) -> bool {\n        self.lower <= id && self.upper >= id\n    }\n\n    pub(crate) fn iter<'txn, 'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        mut result_ids: Option<&mut IntMap<()>>,\n        mut callback: F,\n    ) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        let mut cursor = cursors.get_cursor(self.db)?;\n        cursor.iter_between(\n            &self.lower,\n            &self.upper,\n            false,\n            false,\n            self.sort == Sort::Ascending,\n            |_, id_bytes, object| {\n                let id = id_bytes.to_id();\n                if let Some(result_ids) = result_ids.as_deref_mut() {\n                    if !result_ids.insert_checked(id as u64, ()) {\n                        return Ok(true);\n                    }\n                }\n                let object = IsarObject::from_bytes(object);\n                callback(id, object)\n            },\n        )\n    }\n\n    pub(crate) fn is_overlapping(&self, other: &Self) -> bool {\n        (self.lower <= other.lower && self.upper >= other.upper)\n            || (other.lower <= self.lower && other.upper >= self.upper)\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/index_where_clause.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::{IsarError, Result};\nuse crate::index::index_key::IndexKey;\nuse crate::index::index_key_builder::IndexKeyBuilder;\nuse crate::index::IsarIndex;\nuse crate::mdbx::db::Db;\nuse crate::object::isar_object::IsarObject;\nuse crate::query::Sort;\nuse intmap::IntMap;\n\n#[derive(Clone)]\npub(crate) struct IndexWhereClause {\n    db: Db,\n    index: IsarIndex,\n    lower_key: IndexKey,\n    upper_key: IndexKey,\n    skip_duplicates: bool,\n    sort: Sort,\n}\n\nimpl IndexWhereClause {\n    pub fn new(\n        db: Db,\n        index: IsarIndex,\n        lower_key: IndexKey,\n        upper_key: IndexKey,\n        skip_duplicates: bool,\n        sort: Sort,\n    ) -> Result<Self> {\n        Ok(IndexWhereClause {\n            db,\n            index,\n            lower_key,\n            upper_key,\n            skip_duplicates,\n            sort,\n        })\n    }\n\n    pub fn object_matches(&self, object: IsarObject) -> bool {\n        let mut key_matches = false;\n        let key_builder = IndexKeyBuilder::new(&self.index.properties);\n        key_builder\n            .create_keys(object, |key| {\n                key_matches = key >= &self.lower_key && key <= &self.upper_key;\n                Ok(!key_matches)\n            })\n            .unwrap();\n        key_matches\n    }\n\n    pub fn iter_ids<'txn, 'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        callback: F,\n    ) -> Result<bool>\n    where\n        F: FnMut(i64) -> Result<bool>,\n    {\n        self.index.iter_between(\n            cursors,\n            &self.lower_key,\n            &self.upper_key,\n            self.skip_duplicates,\n            self.sort == Sort::Ascending,\n            callback,\n        )\n    }\n\n    pub fn iter<'txn, 'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        mut result_ids: Option<&mut IntMap<()>>,\n        mut callback: F,\n    ) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        let mut data_cursor = cursors.get_cursor(self.db)?;\n        self.iter_ids(cursors, |id| {\n            if let Some(result_ids) = result_ids.as_deref_mut() {\n                if !result_ids.insert_checked(id as u64, ()) {\n                    return Ok(true);\n                }\n            }\n\n            let entry = data_cursor.move_to(&id)?;\n            let (_, object) = entry.ok_or(IsarError::DbCorrupted {\n                message: \"Could not find object specified in index.\".to_string(),\n            })?;\n            let object = IsarObject::from_bytes(&object);\n\n            callback(id, object)\n        })\n    }\n\n    pub fn is_overlapping(&self, other: &Self) -> bool {\n        self.index != other.index\n            || ((self.lower_key <= other.lower_key && self.upper_key >= other.upper_key)\n                || (other.lower_key <= self.lower_key && other.upper_key >= self.upper_key))\n    }\n\n    pub fn has_duplicates(&self) -> bool {\n        self.index.multi_entry\n    }\n}\n\n/*#[cfg(test)]\nmod tests {\n    //use super::*;\n    //use itertools::Itertools;\n\n    #[macro_export]\n    macro_rules! exec_wc (\n        ($txn:ident, $col:ident, $wc:ident, $res:ident) => {\n            let mut cursor = $col.debug_get_index(0).debug_get_db().cursor(&$txn).unwrap();\n            let $res = $wc.iter(&mut cursor)\n                .unwrap()\n                .map(Result::unwrap)\n                .map(|(_, v)| v)\n                .collect_vec();\n        };\n    );\n\n    /*fn get_str_obj(col: &IsarCollection, str: &str) -> Vec<u8> {\n        let mut ob = col.new_object_builder();\n        ob.write_string(Some(str));\n        ob.finish()\n    }*/\n\n    #[test]\n    fn test_iter() {\n        /*isar!(isar, col => col!(field => String; ind!(field)));\n\n        let txn = isar.begin_txn(true, false).unwrap();\n        let oid1 = col.put(&txn, None, &get_str_obj(&col, \"aaaa\")).unwrap();\n        let oid2 = col.put(&txn, None, &get_str_obj(&col, \"aabb\")).unwrap();\n        let oid3 = col.put(&txn, None, &get_str_obj(&col, \"bbaa\")).unwrap();\n        let oid4 = col.put(&txn, None, &get_str_obj(&col, \"bbbb\")).unwrap();\n\n        let all_oids = &[\n            oid1.as_ref(),\n            oid2.as_ref(),\n            oid3.as_ref(),\n            oid4.as_ref(),\n        ];\n\n        let mut wc = col.new_where_clause(Some(0)).unwrap();\n        exec_wc!(txn, col, wc, oids);\n        assert_eq!(&oids, all_oids);\n\n        wc.add_lower_string_value(Some(\"aa\"), true);\n        exec_wc!(txn, col, wc, oids);\n        assert_eq!(&oids, all_oids);\n\n        let mut wc = col.new_where_clause(Some(0)).unwrap();\n        wc.add_lower_string_value(Some(\"aa\"), false);\n        exec_wc!(txn, col, wc, oids);\n        assert_eq!(&oids, &[oid3.as_ref(), oid4.as_ref()]);\n\n        wc.add_upper_string_value(Some(\"bba\"), true);\n        exec_wc!(txn, col, wc, oids);\n        assert_eq!(&oids, &[oid3.as_ref()]);\n\n        let mut wc = col.new_where_clause(Some(0)).unwrap();\n        wc.add_lower_string_value(Some(\"x\"), false);\n        exec_wc!(txn, col, wc, oids);\n        assert_eq!(&oids, &[] as &[&[u8]]);*/\n    }\n\n    #[test]\n    fn test_add_upper_oid() {}\n}\n*/\n"
  },
  {
    "path": "packages/isar_core/src/query/link_where_clause.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::Result;\nuse crate::link::IsarLink;\nuse crate::object::isar_object::IsarObject;\nuse intmap::IntMap;\n\n#[derive(Clone)]\npub(crate) struct LinkWhereClause {\n    link: IsarLink,\n    id: i64,\n}\n\nimpl LinkWhereClause {\n    pub fn new(link: IsarLink, id: i64) -> Result<Self> {\n        Ok(LinkWhereClause { link, id })\n    }\n\n    pub fn iter<'txn, 'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        mut result_ids: Option<&mut IntMap<()>>,\n        mut callback: F,\n    ) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        self.link.iter(cursors, self.id, |id, object| {\n            if let Some(result_ids) = result_ids.as_deref_mut() {\n                if !result_ids.insert_checked(id as u64, ()) {\n                    return Ok(true);\n                }\n            }\n            callback(id, object)\n        })\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/mod.rs",
    "content": "use intmap::IntMap;\nuse serde_json::{json, Value};\nuse std::cmp::Ordering;\n\nuse crate::collection::IsarCollection;\nuse crate::cursor::IsarCursors;\nuse crate::error::Result;\nuse crate::object::isar_object::IsarObject;\nuse crate::object::json_encode_decode::JsonEncodeDecode;\nuse crate::object::property::Property;\nuse crate::query::filter::Filter;\nuse crate::query::where_clause::WhereClause;\nuse crate::txn::IsarTxn;\n\nmod fast_wild_match;\npub mod filter;\nmod id_where_clause;\nmod index_where_clause;\nmod link_where_clause;\npub mod query_builder;\nmod where_clause;\n\n#[derive(Copy, Clone, Eq, PartialEq)]\npub enum Sort {\n    Ascending,\n    Descending,\n}\n\npub enum Case {\n    Sensitive,\n    Insensitive,\n}\n\n#[derive(Clone)]\npub struct Query {\n    instance_id: u64,\n    where_clauses: Vec<WhereClause>,\n    where_clauses_dup: bool,\n    filter: Option<Filter>,\n    sort: Vec<(Property, Sort)>,\n    distinct: Vec<(Property, bool)>,\n    offset: usize,\n    limit: usize,\n}\n\nimpl<'txn> Query {\n    #[allow(clippy::too_many_arguments)]\n    pub(crate) fn new(\n        instance_id: u64,\n        where_clauses: Vec<WhereClause>,\n        filter: Option<Filter>,\n        sort: Vec<(Property, Sort)>,\n        distinct: Vec<(Property, bool)>,\n        offset: usize,\n        limit: usize,\n    ) -> Self {\n        let where_clauses_dup = Self::check_where_clauses_duplicates(&where_clauses);\n        Query {\n            instance_id,\n            where_clauses,\n            where_clauses_dup,\n            filter,\n            sort,\n            distinct,\n            offset,\n            limit,\n        }\n    }\n\n    fn check_where_clauses_duplicates(where_clauses: &[WhereClause]) -> bool {\n        for (i, wc1) in where_clauses.iter().enumerate() {\n            if wc1.has_duplicates() {\n                return true;\n            }\n            for wc2 in where_clauses.iter().skip(i + 1) {\n                if wc1.is_overlapping(wc2) {\n                    return true;\n                }\n            }\n        }\n        false\n    }\n\n    pub(crate) fn execute_raw<'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        mut callback: F,\n    ) -> Result<()>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        let mut result_ids = if self.where_clauses_dup {\n            Some(IntMap::new())\n        } else {\n            None\n        };\n\n        let static_filter = Filter::stat(true);\n        let filter = self.filter.as_ref().unwrap_or(&static_filter);\n\n        for where_clause in &self.where_clauses {\n            let result = where_clause.iter(cursors, result_ids.as_mut(), |id, object| {\n                if filter.evaluate(id, object, Some(cursors))? {\n                    callback(id, object)\n                } else {\n                    Ok(true)\n                }\n            })?;\n            if !result {\n                return Ok(());\n            }\n        }\n\n        Ok(())\n    }\n\n    fn execute_unsorted<'env, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        callback: F,\n    ) -> Result<()>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        if !self.distinct.is_empty() {\n            let callback = self.add_distinct_unsorted(callback);\n            let callback = self.add_offset_limit_unsorted(callback);\n            self.execute_raw(cursors, callback)\n        } else {\n            let callback = self.add_offset_limit_unsorted(callback);\n            self.execute_raw(cursors, callback)\n        }\n    }\n\n    fn hash_properties(object: IsarObject, properties: &[(Property, bool)]) -> u64 {\n        let mut hash = 0;\n        for (p, case_sensitive) in properties {\n            hash = object.hash_property(p.offset, p.data_type, *case_sensitive, hash);\n        }\n        hash\n    }\n\n    fn add_distinct_unsorted<F>(\n        &self,\n        mut callback: F,\n    ) -> impl FnMut(i64, IsarObject<'txn>) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        let properties = self.distinct.clone();\n        let mut hashes = IntMap::new();\n        move |id, object| {\n            let hash = Self::hash_properties(object, &properties);\n            if hashes.insert_checked(hash, ()) {\n                callback(id, object)\n            } else {\n                Ok(true)\n            }\n        }\n    }\n\n    fn add_offset_limit_unsorted<F>(\n        &self,\n        mut callback: F,\n    ) -> impl FnMut(i64, IsarObject<'txn>) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        let offset = self.offset;\n        let max_count = self.limit.saturating_add(offset);\n        let mut count = 0;\n        move |id, value| {\n            count += 1;\n            if count > max_count || (count > offset && !callback(id, value)?) {\n                Ok(false)\n            } else {\n                Ok(true)\n            }\n        }\n    }\n\n    fn execute_sorted<'env>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n    ) -> Result<Vec<(i64, IsarObject<'txn>)>> {\n        let mut results = vec![];\n        self.execute_raw(cursors, |id, object| {\n            results.push((id, object));\n            Ok(true)\n        })?;\n\n        results.sort_unstable_by(|(_, o1), (_, o2)| {\n            for (p, sort) in &self.sort {\n                let ord = o1.compare_property(o2, p.offset, p.data_type);\n                if ord != Ordering::Equal {\n                    return if *sort == Sort::Ascending {\n                        ord\n                    } else {\n                        ord.reverse()\n                    };\n                }\n            }\n            Ordering::Equal\n        });\n\n        if !self.distinct.is_empty() {\n            Ok(self.add_distinct_sorted(results))\n        } else {\n            Ok(results)\n        }\n    }\n\n    fn add_distinct_sorted(\n        &self,\n        results: Vec<(i64, IsarObject<'txn>)>,\n    ) -> Vec<(i64, IsarObject<'txn>)> {\n        let properties = self.distinct.clone();\n        let mut hashes = IntMap::new();\n        results\n            .into_iter()\n            .filter(|(_, object)| {\n                let hash = Self::hash_properties(*object, &properties);\n                hashes.insert_checked(hash, ())\n            })\n            .collect()\n    }\n\n    fn add_offset_limit_sorted(\n        &self,\n        results: Vec<(i64, IsarObject<'txn>)>,\n    ) -> impl IntoIterator<Item = (i64, IsarObject<'txn>)> {\n        results.into_iter().skip(self.offset).take(self.limit)\n    }\n\n    pub(crate) fn maybe_matches_wc_filter(&self, id: i64, object: IsarObject) -> bool {\n        let maybe_matches = self\n            .where_clauses\n            .iter()\n            .any(|wc| wc.maybe_matches(id, object));\n        if !maybe_matches {\n            return false;\n        }\n\n        if let Some(filter) = &self.filter {\n            filter.evaluate(id, object, None).unwrap_or(true)\n        } else {\n            true\n        }\n    }\n\n    pub fn find_while<F>(&self, txn: &'txn mut IsarTxn, mut callback: F) -> Result<()>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> bool,\n    {\n        txn.read(self.instance_id, |cursors| {\n            if self.sort.is_empty() {\n                self.execute_unsorted(cursors, |id, object| {\n                    let cont = callback(id, object);\n                    Ok(cont)\n                })?;\n            } else {\n                let results = self.execute_sorted(cursors)?;\n                let results_iter = self.add_offset_limit_sorted(results);\n                for (id, object) in results_iter {\n                    if !callback(id, object) {\n                        break;\n                    }\n                }\n            }\n            Ok(())\n        })\n    }\n\n    pub fn find_all_vec(&self, txn: &'txn mut IsarTxn) -> Result<Vec<(i64, IsarObject<'txn>)>> {\n        let mut results = vec![];\n        self.find_while(txn, |id, object| {\n            results.push((id, object));\n            true\n        })?;\n        Ok(results)\n    }\n\n    pub fn count(&self, txn: &mut IsarTxn) -> Result<u32> {\n        let mut counter = 0;\n        self.find_while(txn, |_, _| {\n            counter += 1;\n            true\n        })?;\n        Ok(counter)\n    }\n\n    pub fn export_json(\n        &self,\n        txn: &mut IsarTxn,\n        collection: &IsarCollection,\n        id_name: Option<&str>,\n        primitive_null: bool,\n    ) -> Result<Value> {\n        let mut items = vec![];\n        self.find_while(txn, |id, object| {\n            let mut json = JsonEncodeDecode::encode(\n                &collection.properties,\n                &collection.embedded_properties,\n                object,\n                primitive_null,\n            );\n            if let Some(id_name) = id_name {\n                json.insert(id_name.to_string(), Value::from(id));\n            }\n            items.push(json);\n            true\n        })?;\n        Ok(json!(items))\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/query_builder.rs",
    "content": "use super::index_where_clause::IndexWhereClause;\nuse crate::collection::IsarCollection;\nuse crate::error::{illegal_arg, Result};\nuse crate::index::index_key::IndexKey;\nuse crate::object::property::Property;\nuse crate::query::filter::Filter;\nuse crate::query::id_where_clause::IdWhereClause;\nuse crate::query::link_where_clause::LinkWhereClause;\nuse crate::query::where_clause::WhereClause;\nuse crate::query::{Query, Sort};\n\npub struct QueryBuilder<'a> {\n    pub collection: &'a IsarCollection,\n    where_clauses: Option<Vec<WhereClause>>,\n    filter: Option<Filter>,\n    sort: Vec<(Property, Sort)>,\n    distinct: Vec<(Property, bool)>,\n    offset: usize,\n    limit: usize,\n}\n\nimpl<'a> QueryBuilder<'a> {\n    pub(crate) fn new(collection: &'a IsarCollection) -> QueryBuilder {\n        QueryBuilder {\n            collection,\n            where_clauses: None,\n            filter: None,\n            sort: vec![],\n            distinct: vec![],\n            offset: 0,\n            limit: usize::MAX,\n        }\n    }\n\n    fn init_where_clauses(&mut self) {\n        if self.where_clauses.is_none() {\n            self.where_clauses = Some(vec![]);\n        }\n    }\n\n    pub fn add_id_where_clause(&mut self, start: i64, end: i64) -> Result<()> {\n        self.init_where_clauses();\n        let (lower, upper, sort) = if start > end {\n            (end, start, Sort::Descending)\n        } else {\n            (start, end, Sort::Ascending)\n        };\n        let wc = IdWhereClause::new(self.collection.db, lower, upper, sort);\n        if !wc.is_empty() {\n            self.where_clauses\n                .as_mut()\n                .unwrap()\n                .push(WhereClause::Id(wc))\n        }\n        Ok(())\n    }\n\n    pub fn add_index_where_clause(\n        &mut self,\n        index_id: u64,\n        lower: IndexKey,\n        upper: IndexKey,\n        sort: Sort,\n        skip_duplicates: bool,\n    ) -> Result<()> {\n        self.init_where_clauses();\n        let index = self.collection.get_index_by_id(index_id)?;\n        let wc = IndexWhereClause::new(\n            self.collection.db,\n            index.clone(),\n            lower,\n            upper,\n            skip_duplicates,\n            sort,\n        )?;\n        self.where_clauses\n            .as_mut()\n            .unwrap()\n            .push(WhereClause::Index(wc));\n\n        Ok(())\n    }\n\n    pub fn add_link_where_clause(\n        &mut self,\n        collection: &IsarCollection,\n        link_id: u64,\n        id: i64,\n    ) -> Result<()> {\n        let link = collection.get_link_backlink(link_id)?;\n\n        self.init_where_clauses();\n        let wc = LinkWhereClause::new(link.clone(), id)?;\n        self.where_clauses\n            .as_mut()\n            .unwrap()\n            .push(WhereClause::Link(wc));\n        Ok(())\n    }\n\n    pub fn set_filter(&mut self, filter: Filter) {\n        self.filter = Some(filter);\n    }\n\n    pub fn add_sort(&mut self, property: &Property, sort: Sort) -> Result<()> {\n        if property.data_type.is_scalar() {\n            self.sort.push((property.clone(), sort));\n            Ok(())\n        } else {\n            illegal_arg(\"Only scalar types may be used for sorting.\")\n        }\n    }\n\n    pub fn add_distinct(&mut self, property: &Property, case_sensitive: bool) {\n        self.distinct.push((property.clone(), case_sensitive));\n    }\n\n    pub fn set_offset(&mut self, offset: usize) {\n        self.offset = offset;\n    }\n\n    pub fn set_limit(&mut self, limit: usize) {\n        self.limit = limit;\n    }\n\n    pub fn build(mut self) -> Query {\n        if self.where_clauses.is_none() {\n            self.add_id_where_clause(i64::MIN, i64::MAX).unwrap();\n        }\n        Query::new(\n            self.collection.instance_id,\n            self.where_clauses.unwrap(),\n            self.filter,\n            self.sort,\n            self.distinct,\n            self.offset,\n            self.limit,\n        )\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/query/where_clause.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::Result;\nuse crate::object::isar_object::IsarObject;\nuse crate::query::id_where_clause::IdWhereClause;\nuse crate::query::index_where_clause::IndexWhereClause;\nuse crate::query::link_where_clause::LinkWhereClause;\nuse intmap::IntMap;\n\n#[derive(Clone)]\npub(crate) enum WhereClause {\n    Id(IdWhereClause),\n    Index(IndexWhereClause),\n    Link(LinkWhereClause),\n}\n\nimpl WhereClause {\n    pub fn maybe_matches(&self, id: i64, object: IsarObject) -> bool {\n        match self {\n            WhereClause::Id(wc) => wc.id_matches(id),\n            WhereClause::Index(wc) => wc.object_matches(object),\n            WhereClause::Link(_) => true,\n        }\n    }\n\n    pub fn iter<'txn, 'env, 'a, F>(\n        &self,\n        cursors: &IsarCursors<'txn, 'env>,\n        result_ids: Option<&mut IntMap<()>>,\n        callback: F,\n    ) -> Result<bool>\n    where\n        F: FnMut(i64, IsarObject<'txn>) -> Result<bool>,\n    {\n        match self {\n            WhereClause::Id(wc) => wc.iter(cursors, result_ids, callback),\n            WhereClause::Index(wc) => wc.iter(cursors, result_ids, callback),\n            WhereClause::Link(wc) => wc.iter(cursors, result_ids, callback),\n        }\n    }\n\n    pub(crate) fn is_overlapping(&self, other: &Self) -> bool {\n        match (self, other) {\n            (WhereClause::Id(wc1), WhereClause::Id(wc2)) => wc1.is_overlapping(wc2),\n            (WhereClause::Index(wc1), WhereClause::Index(wc2)) => wc1.is_overlapping(wc2),\n            _ => true,\n        }\n    }\n\n    pub(crate) fn has_duplicates(&self) -> bool {\n        match self {\n            WhereClause::Id(_) => false,\n            WhereClause::Index(wc) => wc.has_duplicates(),\n            WhereClause::Link(_) => false,\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/schema/collection_schema.rs",
    "content": "use crate::error::{schema_error, IsarError, Result};\nuse crate::object::data_type::DataType;\nuse crate::object::property::Property;\nuse crate::schema::index_schema::{IndexSchema, IndexType};\nuse crate::schema::link_schema::LinkSchema;\nuse crate::schema::property_schema::PropertySchema;\nuse itertools::Itertools;\nuse serde::{Deserialize, Serialize};\n\nuse super::schema_manager::SchemaManager;\n\n#[derive(Serialize, Deserialize, Clone, Eq)]\npub struct CollectionSchema {\n    pub(crate) name: String,\n    #[serde(default)]\n    pub(crate) embedded: bool,\n    pub(crate) properties: Vec<PropertySchema>,\n    #[serde(default)]\n    pub(crate) indexes: Vec<IndexSchema>,\n    #[serde(default)]\n    pub(crate) links: Vec<LinkSchema>,\n    #[serde(default)]\n    pub(crate) version: u8,\n}\n\nimpl PartialEq for CollectionSchema {\n    fn eq(&self, other: &Self) -> bool {\n        self.name == other.name && self.embedded == other.embedded\n    }\n}\n\nimpl CollectionSchema {\n    pub fn new(\n        name: &str,\n        embedded: bool,\n        properties: Vec<PropertySchema>,\n        indexes: Vec<IndexSchema>,\n        links: Vec<LinkSchema>,\n    ) -> CollectionSchema {\n        CollectionSchema {\n            name: name.to_string(),\n            embedded,\n            properties,\n            indexes,\n            links,\n            version: SchemaManager::ISAR_FILE_VERSION,\n        }\n    }\n\n    fn verify_name(name: &str) -> Result<()> {\n        if name.is_empty() {\n            schema_error(\"Empty names are not allowed.\")\n        } else if name.starts_with('_') {\n            schema_error(\"Names must not begin with an underscore.\")\n        } else {\n            Ok(())\n        }\n    }\n\n    pub(crate) fn verify(&self, collections: &[CollectionSchema]) -> Result<()> {\n        Self::verify_name(&self.name)?;\n\n        if self.embedded && (!self.links.is_empty() || !self.indexes.is_empty()) {\n            schema_error(\"Embedded objects must not have Links or Indexes.\")?;\n        }\n\n        let verify_target_col_exists = |col: &str, embedded: bool| -> Result<()> {\n            if !collections\n                .iter()\n                .any(|c| c.name == col && c.embedded == embedded)\n            {\n                schema_error(\"Target collection does not exist.\")?;\n            }\n            Ok(())\n        };\n\n        for property in &self.properties {\n            if let Some(name) = &property.name {\n                Self::verify_name(name)?;\n            }\n\n            if property.data_type == DataType::Object || property.data_type == DataType::ObjectList\n            {\n                if let Some(target_col) = &property.target_col {\n                    verify_target_col_exists(target_col, true)?;\n                } else {\n                    schema_error(\"Object property must have a target collection.\")?;\n                }\n            } else {\n                if property.target_col.is_some() {\n                    schema_error(\"Target collection can only be set for object properties.\")?;\n                }\n            }\n        }\n\n        for link in &self.links {\n            Self::verify_name(&link.name)?;\n            verify_target_col_exists(&link.target_col, false)?;\n        }\n\n        let property_names = self\n            .properties\n            .iter()\n            .unique_by(|p| p.name.as_ref().unwrap());\n        if property_names.count() != self.properties.len() {\n            schema_error(\"Duplicate property name\")?;\n        }\n\n        let index_names = self.indexes.iter().unique_by(|i| i.name.as_str());\n        if index_names.count() != self.indexes.len() {\n            schema_error(\"Duplicate index name\")?;\n        }\n\n        let link_names = self.links.iter().unique_by(|l| l.name.as_str());\n        if link_names.count() != self.links.len() {\n            schema_error(\"Duplicate link name\")?;\n        }\n\n        for index in &self.indexes {\n            if index.properties.is_empty() {\n                schema_error(\"At least one property needs to be added to a valid index\")?;\n            } else if index.properties.len() > 3 {\n                schema_error(\"No more than three properties may be used as a composite index\")?;\n            }\n\n            if !index.unique && index.replace {\n                schema_error(\"Only unique indexes can replace\")?;\n            }\n\n            for (i, index_property) in index.properties.iter().enumerate() {\n                let property = self\n                    .properties\n                    .iter()\n                    .find(|p| p.name.as_ref() == Some(&index_property.name));\n                if property.is_none() {\n                    schema_error(\"IsarIndex property does not exist\")?;\n                }\n                let property = property.unwrap();\n\n                if property.data_type == DataType::Object\n                    || property.data_type == DataType::ObjectList\n                {\n                    schema_error(\"Object and ObjectList cannot be indexed.\")?;\n                }\n\n                if property.data_type == DataType::Float\n                    || property.data_type == DataType::Double\n                    || property.data_type == DataType::FloatList\n                    || property.data_type == DataType::DoubleList\n                {\n                    if index_property.index_type == IndexType::Hash {\n                        schema_error(\"Float values cannot be hashed.\")?;\n                    } else if i != index.properties.len() - 1 {\n                        schema_error(\n                            \"Float indexes must only be at the end of a composite index.\",\n                        )?;\n                    }\n                }\n\n                if property.data_type.get_element_type().is_some() {\n                    if index.properties.len() > 1 && index_property.index_type != IndexType::Hash {\n                        schema_error(\"Composite list indexes are not supported.\")?;\n                    }\n                } else if property.data_type == DataType::String\n                    && i != index.properties.len() - 1\n                    && index_property.index_type != IndexType::Hash\n                {\n                    schema_error(\n                        \"Non-hashed string indexes must only be at the end of a composite index.\",\n                    )?;\n                }\n\n                if property.data_type != DataType::String\n                    && property.data_type.get_element_type().is_none()\n                    && index_property.index_type == IndexType::Hash\n                {\n                    schema_error(\"Only string and list indexes may be hashed\")?;\n                }\n                if property.data_type != DataType::StringList\n                    && index_property.index_type == IndexType::HashElements\n                {\n                    schema_error(\"Only string list indexes may be use hash elements\")?;\n                }\n                if property.data_type != DataType::String\n                    && property.data_type != DataType::StringList\n                    && index_property.case_sensitive\n                {\n                    schema_error(\"Only String and StringList indexes may be case sensitive.\")?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(crate) fn merge_properties(&mut self, existing: &Self) -> Result<Vec<String>> {\n        let mut properties = existing.properties.clone();\n        let mut removed_properties = vec![];\n\n        for property in &mut properties {\n            if property.name.is_some() && !self.properties.contains(property) {\n                removed_properties.push(property.name.take().unwrap());\n            }\n        }\n        for property in &self.properties {\n            if !properties.contains(property) {\n                properties.push(property.clone())\n            }\n        }\n\n        self.properties = properties;\n\n        Ok(removed_properties)\n    }\n\n    pub fn get_properties(&self) -> Vec<Property> {\n        let mut properties = vec![];\n        let mut offset = 2;\n        for property_schema in self.properties.iter() {\n            let property = property_schema.as_property(offset);\n            if let Some(property) = property {\n                properties.push(property);\n            }\n            offset += property_schema.data_type.get_static_size();\n        }\n\n        properties.sort_by(|a, b| a.name.cmp(&b.name));\n        properties\n    }\n\n    pub fn to_json_bytes(&self) -> Result<Vec<u8>> {\n        serde_json::to_vec(self).map_err(|_| IsarError::SchemaError {\n            message: \"Could not serialize schema.\".to_string(),\n        })\n    }\n}\n\n/*#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_add_property_empty_name() {\n        let mut col = CollectionSchema::new(\"col\");\n        assert!(col.add_property(\"\", DataType::Int).is_err())\n    }\n\n    #[test]\n    fn test_add_property_duplicate_name() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"prop\", DataType::Int).unwrap();\n        assert!(col.add_property(\"prop\", DataType::Int).is_err())\n    }\n\n    #[test]\n    fn test_add_property_same_type_wrong_order() {\n        let mut col = CollectionSchema::new(\"col\");\n\n        col.add_property(\"b\", DataType::Int).unwrap();\n        assert!(col.add_property(\"a\", DataType::Int).is_err())\n    }\n\n    #[test]\n    fn test_add_property_wrong_order() {\n        let mut col = CollectionSchema::new(\"col\");\n\n        col.add_property(\"a\", DataType::Long).unwrap();\n        assert!(col.add_property(\"b\", DataType::Int).is_err())\n    }\n\n    #[test]\n    fn test_add_index_without_properties() {\n        let mut col = CollectionSchema::new(\"col\");\n\n        assert!(col.add_index(&[], false, false).is_err())\n    }\n\n    #[test]\n    fn test_add_index_with_non_existing_property() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"prop1\", DataType::Int).unwrap();\n\n        col.add_index(&[\"prop1\"], false, false).unwrap();\n        assert!(col.add_index(&[\"wrongprop\"], false, false).is_err())\n    }\n\n    #[test]\n    fn test_add_index_with_illegal_data_type() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byte\", DataType::Byte).unwrap();\n        col.add_property(\"int\", DataType::Int).unwrap();\n        col.add_property(\"float\", DataType::Float).unwrap();\n        col.add_property(\"long\", DataType::Long).unwrap();\n        col.add_property(\"double\", DataType::Double).unwrap();\n        col.add_property(\"str\", DataType::String).unwrap();\n        col.add_property(\"byteList\", DataType::ByteList).unwrap();\n        col.add_property(\"intList\", DataType::IntList).unwrap();\n\n        col.add_index(&[\"byte\"], false, None, false).unwrap();\n        col.add_index(&[\"int\"], false, None, false).unwrap();\n        col.add_index(&[\"float\"], false, None, false).unwrap();\n        col.add_index(&[\"long\"], false, None, false).unwrap();\n        col.add_index(&[\"double\"], false, None, false).unwrap();\n        col.add_index(&[\"str\"], false, Some(StringIndexType::Value), false)\n            .unwrap();\n        assert!(col.add_index(&[\"byteList\"], false, false).is_err());\n        assert!(col.add_index(&[\"intList\"], false, false).is_err());\n    }\n\n    #[test]\n    fn test_add_index_too_many_properties() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"prop1\", DataType::Int).unwrap();\n        col.add_property(\"prop2\", DataType::Int).unwrap();\n        col.add_property(\"prop3\", DataType::Int).unwrap();\n        col.add_property(\"prop4\", DataType::Int).unwrap();\n\n        assert!(col\n            .add_index(&[\"prop1\", \"prop2\", \"prop3\", \"prop4\"], false, false)\n            .is_err())\n    }\n\n    #[test]\n    fn test_add_duplicate_index() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"prop1\", DataType::Int).unwrap();\n        col.add_property(\"prop2\", DataType::Int).unwrap();\n\n        col.add_index(&[\"prop2\"], false, false).unwrap();\n        col.add_index(&[\"prop1\", \"prop2\"], false, false).unwrap();\n        assert!(col.add_index(&[\"prop1\", \"prop2\"], false, false).is_err());\n        assert!(col.add_index(&[\"prop1\"], false, false).is_err());\n    }\n\n    #[test]\n    fn test_add_composite_index_with_non_hashed_string_in_the_middle() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"int\", DataType::Int).unwrap();\n        col.add_property(\"str\", DataType::String).unwrap();\n\n        col.add_index(&[\"int\", \"str\"], false, false).unwrap();\n        assert!(col.add_index(&[\"str\", \"int\"], false, false).is_err());\n        col.add_index(&[\"str\", \"int\"], false, true).unwrap();\n    }\n\n    #[test]\n    fn test_properties_have_correct_offset() {\n        fn get_offsets(mut schema: CollectionSchema) -> Vec<usize> {\n            let mut get_id = || 1;\n            schema.update_with_existing_collections(&[], &mut get_id);\n            let col = schema.get_isar_collection();\n            let mut offsets = vec![];\n            for i in 0..schema.properties.len() {\n                let (_, p) = col.get_properties().get(i).unwrap();\n                offsets.push(p.offset);\n            }\n            offsets\n        }\n\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byte\", DataType::Byte).unwrap();\n        col.add_property(\"int\", DataType::Int).unwrap();\n        col.add_property(\"double\", DataType::Double).unwrap();\n        assert_eq!(get_offsets(col), vec![0, 2, 10]);\n\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byte1\", DataType::Byte).unwrap();\n        col.add_property(\"byte2\", DataType::Byte).unwrap();\n        col.add_property(\"byte3\", DataType::Byte).unwrap();\n        col.add_property(\"str\", DataType::String).unwrap();\n        assert_eq!(get_offsets(col), vec![0, 1, 2, 10]);\n\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byteList\", DataType::ByteList).unwrap();\n        col.add_property(\"intList\", DataType::IntList).unwrap();\n        col.add_property(\"doubleList\", DataType::DoubleList)\n            .unwrap();\n        assert_eq!(get_offsets(col), vec![2, 10, 18]);\n    }\n\n    #[test]\n    fn update_with_no_existing_collection() {\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byte\", DataType::Byte).unwrap();\n        col.add_property(\"int\", DataType::Int).unwrap();\n        col.add_index(&[\"byte\"], true, false).unwrap();\n        col.add_index(&[\"int\"], true, false).unwrap();\n\n        let mut counter = 0;\n        let mut get_id = || {\n            counter += 1;\n            counter\n        };\n        col.update_with_existing_collections(&[], &mut get_id);\n\n        assert_eq!(col.id, Some(1));\n        assert_eq!(col.indexes[0].id, Some(2));\n        assert_eq!(col.indexes[1].id, Some(3));\n    }\n\n    #[test]\n    fn update_with_existing_collection() {\n        let mut counter = 0;\n        let mut get_id = || {\n            counter += 1;\n            counter\n        };\n\n        let mut col1 = CollectionSchema::new(\"col\");\n        col1.add_property(\"byte\", DataType::Byte).unwrap();\n        col1.add_property(\"int\", DataType::Int).unwrap();\n        col1.add_index(&[\"byte\"], true, false).unwrap();\n        col1.add_index(&[\"int\"], true, false).unwrap();\n\n        col1.update_with_existing_collections(&[], &mut get_id);\n        assert_eq!(col1.id, Some(1));\n        assert_eq!(col1.indexes[0].id, Some(2));\n        assert_eq!(col1.indexes[1].id, Some(3));\n\n        let mut col2 = CollectionSchema::new(\"col\");\n        col2.add_property(\"byte\", DataType::Byte).unwrap();\n        col2.add_property(\"int\", DataType::Int).unwrap();\n        col2.add_index(&[\"byte\"], true, false).unwrap();\n        col2.add_index(&[\"int\", \"byte\"], true, false).unwrap();\n\n        col2.update_with_existing_collections(&[col1], &mut get_id);\n        assert_eq!(col2.id, Some(1));\n        assert_eq!(col2.indexes[0].id, Some(2));\n        assert_eq!(col2.indexes[1].id, Some(4));\n\n        let mut col3 = CollectionSchema::new(\"col3\");\n        col3.update_with_existing_collections(&[col2], &mut get_id);\n        assert_eq!(col3.id, Some(5));\n    }\n}\n*/\n"
  },
  {
    "path": "packages/isar_core/src/schema/index_schema.rs",
    "content": "use crate::index::{IndexProperty, IsarIndex};\nuse crate::mdbx::db::Db;\nuse crate::object::property::Property;\nuse itertools::Itertools;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]\npub enum IndexType {\n    Value,\n    Hash,\n    HashElements,\n}\n\n#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]\npub struct IndexPropertySchema {\n    pub(crate) name: String,\n    #[serde(rename = \"type\")]\n    pub(crate) index_type: IndexType,\n    #[serde(rename = \"caseSensitive\")]\n    pub(crate) case_sensitive: bool,\n}\n\nimpl IndexPropertySchema {\n    pub fn new(name: &str, index_type: IndexType, case_sensitive: bool) -> IndexPropertySchema {\n        IndexPropertySchema {\n            name: name.to_string(),\n            index_type,\n            case_sensitive,\n        }\n    }\n}\n\n#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]\npub struct IndexSchema {\n    pub(crate) name: String,\n    pub(crate) properties: Vec<IndexPropertySchema>,\n    pub(crate) unique: bool,\n    #[serde(default)]\n    pub(crate) replace: bool,\n}\n\nimpl IndexSchema {\n    pub fn new(\n        name: &str,\n        properties: Vec<IndexPropertySchema>,\n        unique: bool,\n        replace: bool,\n    ) -> IndexSchema {\n        IndexSchema {\n            name: name.to_string(),\n            properties,\n            unique,\n            replace,\n        }\n    }\n\n    pub(crate) fn as_index(&self, db: Db, properties: &[Property]) -> IsarIndex {\n        let index_properties = self\n            .properties\n            .iter()\n            .map(|ip| {\n                let property = properties.iter().find(|p| ip.name == *p.name).unwrap();\n                IndexProperty::new(property.clone(), ip.index_type, ip.case_sensitive)\n            })\n            .collect_vec();\n        IsarIndex::new(&self.name, db, index_properties, self.unique, self.replace)\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/schema/link_schema.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]\npub struct LinkSchema {\n    pub(crate) name: String,\n    #[serde(rename = \"target\")]\n    pub(crate) target_col: String,\n}\n\nimpl LinkSchema {\n    pub fn new(name: &str, target_collection_name: &str) -> Self {\n        LinkSchema {\n            name: name.to_string(),\n            target_col: target_collection_name.to_string(),\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/schema/migrate_v1.rs",
    "content": "use itertools::Itertools;\n\nuse crate::legacy::isar_object_v1::{LegacyIsarObject, LegacyProperty};\nuse crate::object::data_type::DataType;\nuse crate::object::id::BytesToId;\nuse crate::object::isar_object::IsarObject;\nuse crate::object::object_builder::ObjectBuilder;\nuse crate::schema::schema_manager::SchemaManager;\nuse crate::{cursor::IsarCursors, error::Result, mdbx::txn::Txn};\n\nuse super::collection_schema::CollectionSchema;\n\npub fn migrate_v1(txn: &Txn, schema: &mut CollectionSchema) -> Result<()> {\n    let cursors = IsarCursors::new(txn, vec![]);\n    let mut buffer = Some(vec![]);\n\n    for index in &schema.indexes {\n        let index_db = SchemaManager::open_index_db(txn, schema, index)?;\n        index_db.clear(txn)?;\n    }\n    schema.indexes.clear();\n\n    let props = schema.get_properties();\n\n    let mut offset = 2;\n    let legacy_props = schema\n        .properties\n        .iter()\n        .map(|p| {\n            let property = LegacyProperty::new(p.data_type, offset);\n            offset += match p.data_type {\n                DataType::Byte => 1,\n                DataType::Int | DataType::Float => 4,\n                _ => 8,\n            };\n\n            property\n        })\n        .collect_vec();\n\n    let db = SchemaManager::open_collection_db(txn, schema)?;\n    let mut db_cursor = cursors.get_cursor(db)?;\n    db_cursor.iter_all(false, true, |cursor, id_bytes, obj| {\n        // We need to copy the data here because it will become invalid during the write\n        let id = id_bytes.to_id();\n        let obj = obj.to_vec();\n\n        let legacy_object = LegacyIsarObject::from_bytes(&obj);\n        let mut new_object = ObjectBuilder::new(&props, buffer.take());\n        for (prop, legacy_prop) in props.iter().zip(&legacy_props) {\n            match prop.data_type {\n                DataType::Bool => {\n                    if legacy_object.is_null(*legacy_prop) {\n                        new_object.write_bool(prop.offset, None);\n                    } else {\n                        new_object\n                            .write_bool(prop.offset, Some(legacy_object.read_bool(*legacy_prop)))\n                    }\n                }\n                DataType::Byte => {\n                    new_object.write_byte(prop.offset, legacy_object.read_byte(*legacy_prop))\n                }\n                DataType::Int => {\n                    new_object.write_int(prop.offset, legacy_object.read_int(*legacy_prop))\n                }\n                DataType::Float => {\n                    new_object.write_float(prop.offset, legacy_object.read_float(*legacy_prop))\n                }\n                DataType::Long => {\n                    new_object.write_long(prop.offset, legacy_object.read_long(*legacy_prop))\n                }\n                DataType::Double => {\n                    new_object.write_double(prop.offset, legacy_object.read_double(*legacy_prop))\n                }\n                DataType::String => {\n                    new_object.write_string(prop.offset, legacy_object.read_string(*legacy_prop))\n                }\n                DataType::BoolList => {\n                    let byte_list = legacy_object.read_byte_list(*legacy_prop);\n                    let bool_list = byte_list.map(|bytes| {\n                        bytes\n                            .into_iter()\n                            .map(|b| IsarObject::byte_to_bool(*b))\n                            .collect_vec()\n                    });\n                    new_object.write_bool_list(prop.offset, bool_list.as_deref())\n                }\n                DataType::ByteList => new_object\n                    .write_byte_list(prop.offset, legacy_object.read_byte_list(*legacy_prop)),\n                DataType::IntList => new_object.write_int_list(\n                    prop.offset,\n                    legacy_object.read_int_list(*legacy_prop).as_deref(),\n                ),\n                DataType::FloatList => new_object.write_float_list(\n                    prop.offset,\n                    legacy_object.read_float_list(*legacy_prop).as_deref(),\n                ),\n                DataType::LongList => new_object.write_long_list(\n                    prop.offset,\n                    legacy_object.read_long_list(*legacy_prop).as_deref(),\n                ),\n                DataType::DoubleList => new_object.write_double_list(\n                    prop.offset,\n                    legacy_object.read_double_list(*legacy_prop).as_deref(),\n                ),\n                DataType::StringList => new_object.write_string_list(\n                    prop.offset,\n                    legacy_object.read_string_list(*legacy_prop).as_deref(),\n                ),\n                _ => unreachable!(),\n            }\n        }\n\n        cursor.put(&id, new_object.finish().as_bytes())?;\n        buffer.replace(new_object.recycle());\n        Ok(true)\n    })?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "packages/isar_core/src/schema/mod.rs",
    "content": "pub mod collection_schema;\npub mod index_schema;\npub mod link_schema;\npub(crate) mod migrate_v1;\npub mod property_schema;\npub(crate) mod schema_manager;\n\nuse crate::error::{schema_error, Result};\nuse crate::schema::collection_schema::CollectionSchema;\nuse itertools::Itertools;\nuse serde::{Deserialize, Serialize};\nuse xxhash_rust::xxh3::xxh3_64_with_seed;\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct Schema {\n    pub(crate) collections: Vec<CollectionSchema>,\n}\n\nimpl Schema {\n    pub fn new(collections: Vec<CollectionSchema>) -> Result<Schema> {\n        let collection_names = collections.iter().unique_by(|c| &c.name);\n        if collection_names.count() != collections.len() {\n            schema_error(\"Duplicate collection name\")?;\n        }\n        for col in &collections {\n            col.verify(&collections)?;\n        }\n\n        let schema = Schema { collections };\n        Ok(schema)\n    }\n\n    pub fn from_json(json: &[u8]) -> Result<Schema> {\n        if let Ok(collections) = serde_json::from_slice::<Vec<CollectionSchema>>(json) {\n            Schema::new(collections)\n        } else {\n            schema_error(\"Could not deserialize schema JSON\")\n        }\n    }\n\n    pub(crate) fn get_collection(&self, name: &str, embedded: bool) -> Option<&CollectionSchema> {\n        self.collections\n            .iter()\n            .find(|c| c.name == name && c.embedded == embedded)\n    }\n\n    pub(crate) fn count_dbs(&self) -> usize {\n        let mut count = 0;\n        for col in &self.collections {\n            count += 1;\n            count += col.indexes.len();\n            count += col.links.len() * 2;\n        }\n        count\n    }\n}\n\n/*#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::object::data_type::DataType;\n\n    #[test]\n    fn test_add_collection() {\n        let mut schema = Schema::new();\n\n        let col1 = CollectionSchema::new(\"col\");\n        schema.add_collection(col1).unwrap();\n\n        let col2 = CollectionSchema::new(\"other\");\n        schema.add_collection(col2).unwrap();\n\n        let duplicate = CollectionSchema::new(\"col\");\n        assert!(schema.add_collection(duplicate).is_err());\n    }\n\n    #[test]\n    fn test_update_with_existing_schema() -> Result<()> {\n        let mut schema1 = Schema::new();\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byteProperty\", DataType::Byte)?;\n        col.add_property(\"intProperty\", DataType::Int)?;\n        col.add_property(\"longProperty\", DataType::Long)?;\n        col.add_property(\"stringProperty\", DataType::String)?;\n        col.add_index(&[\"byteProperty\"], false, false)?;\n        col.add_index(&[\"intProperty\", \"byteProperty\"], true, false)?;\n        col.add_index(&[\"longProperty\"], false, false)?;\n        col.add_index(&[\"intProperty\", \"longProperty\"], false, false)?;\n        col.add_index(&[\"stringProperty\"], false, true)?;\n        schema1.add_collection(col)?;\n\n        let mut counter = 0;\n        let get_id = || {\n            counter += 1;\n            counter\n        };\n        schema1.update_with_existing_schema_internal(None, get_id);\n        let col = &schema1.collections[0];\n        assert_eq!(col.id, Some(1));\n        assert_eq!(col.indexes[0].id, Some(2));\n        assert_eq!(col.indexes[1].id, Some(3));\n        assert_eq!(col.indexes[2].id, Some(4));\n        assert_eq!(col.indexes[3].id, Some(5));\n        assert_eq!(col.indexes[4].id, Some(6));\n\n        let mut schema2 = Schema::new();\n        let mut col = CollectionSchema::new(\"col\");\n        col.add_property(\"byteProperty\", DataType::Byte)?;\n        col.add_property(\"intProperty\", DataType::Int)?;\n        col.add_property(\"longProperty\", DataType::Double)?; // changed type\n        col.add_property(\"stringProperty\", DataType::String)?;\n        col.add_index(&[\"byteProperty\"], false, false)?;\n        col.add_index(&[\"intProperty\", \"byteProperty\"], false, false)?; // changed unique\n        col.add_index(&[\"longProperty\"], false, false)?; // changed property type\n        col.add_index(&[\"intProperty\", \"longProperty\"], false, false)?; // changed property type-\n        col.add_index(&[\"stringProperty\"], false, false)?; // changed hash_value\n        schema2.add_collection(col)?;\n\n        let mut counter = 0;\n        let get_id = || {\n            counter += 1;\n            counter\n        };\n        schema2.update_with_existing_schema_internal(Some(&schema1), get_id);\n        let col = &schema2.collections[0];\n        assert_eq!(col.id, Some(1));\n        assert_eq!(col.indexes[0].id, Some(2));\n        assert_eq!(col.indexes[1].id, Some(7));\n        assert_eq!(col.indexes[2].id, Some(8));\n        assert_eq!(col.indexes[3].id, Some(9));\n        assert_eq!(col.indexes[4].id, Some(10));\n\n        Ok(())\n    }\n}\n*/\n"
  },
  {
    "path": "packages/isar_core/src/schema/property_schema.rs",
    "content": "use crate::object::data_type::DataType;\nuse crate::object::property::Property;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize, Clone, Eq)]\npub struct PropertySchema {\n    pub(crate) name: Option<String>,\n    #[serde(rename = \"type\")]\n    pub(crate) data_type: DataType,\n    #[serde(default)]\n    #[serde(rename = \"target\")]\n    pub(crate) target_col: Option<String>,\n}\n\nimpl PropertySchema {\n    pub fn new(\n        name: Option<String>,\n        data_type: DataType,\n        target_col: Option<String>,\n    ) -> PropertySchema {\n        PropertySchema {\n            name,\n            data_type,\n            target_col,\n        }\n    }\n\n    pub(crate) fn as_property(&self, offset: usize) -> Option<Property> {\n        if let Some(name) = &self.name {\n            let p = Property::new(name, self.data_type, offset, self.target_col.as_deref());\n            Some(p)\n        } else {\n            None\n        }\n    }\n}\n\nimpl PartialEq for PropertySchema {\n    fn eq(&self, other: &Self) -> bool {\n        let type_bool_byte = (self.data_type == DataType::Bool || self.data_type == DataType::Byte)\n            && (other.data_type == DataType::Bool || other.data_type == DataType::Byte);\n\n        let type_bool_byte_list = (self.data_type == DataType::BoolList\n            || self.data_type == DataType::ByteList)\n            && (other.data_type == DataType::BoolList || other.data_type == DataType::ByteList);\n\n        let type_eq = self.data_type == other.data_type || type_bool_byte || type_bool_byte_list;\n\n        self.name == other.name && type_eq && self.target_col == other.target_col\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/schema/schema_manager.rs",
    "content": "use super::collection_schema::CollectionSchema;\nuse super::index_schema::IndexSchema;\nuse super::link_schema::LinkSchema;\nuse super::Schema;\nuse crate::collection::IsarCollection;\nuse crate::cursor::IsarCursors;\nuse crate::error::{schema_error, IsarError, Result};\nuse crate::index::index_key::IndexKey;\nuse crate::index::IsarIndex;\nuse crate::link::IsarLink;\nuse crate::mdbx::cursor::{Cursor, UnboundCursor};\nuse crate::mdbx::{db::Db, txn::Txn};\nuse crate::object::property::Property;\nuse crate::schema::migrate_v1::migrate_v1;\nuse intmap::IntMap;\nuse once_cell::sync::Lazy;\nuse std::ops::Deref;\nuse xxhash_rust::xxh3::xxh3_64;\n\nstatic OLD_INFO_VERSION_KEY: Lazy<IndexKey> = Lazy::new(|| {\n    let mut key = IndexKey::new();\n    key.add_string(Some(\"version\"), true);\n    key\n});\n\nstatic OLD_INFO_SCHEMA_KEY: Lazy<IndexKey> = Lazy::new(|| {\n    let mut key = IndexKey::new();\n    key.add_string(Some(\"schema\"), true);\n    key\n});\n\npub(crate) struct SchemaManager {\n    instance_id: u64,\n    info_db: Db,\n    pub schemas: Vec<CollectionSchema>,\n}\n\nimpl SchemaManager {\n    pub const ISAR_FILE_VERSION: u8 = 2;\n\n    pub fn create(instance_id: u64, txn: &Txn) -> Result<Self> {\n        let info_db = Db::open(txn, Some(\"_info\"), false, false, false)?;\n        let mut info_cursor = UnboundCursor::new().bind(txn, info_db)?;\n\n        Self::migrate_old_info(&mut info_cursor)?;\n\n        let schemas = Self::get_schemas(&mut info_cursor)?;\n        let manager = SchemaManager {\n            instance_id,\n            info_db,\n            schemas,\n        };\n        Ok(manager)\n    }\n\n    fn migrate_old_info(info_cursor: &mut Cursor) -> Result<()> {\n        let version = info_cursor.move_to(OLD_INFO_VERSION_KEY.deref())?;\n        if let Some((_, version)) = version {\n            let version_num = u64::from_le_bytes(version.try_into().unwrap());\n            info_cursor.delete_current()?;\n\n            let schema_bytes = info_cursor.move_to(OLD_INFO_SCHEMA_KEY.deref())?;\n            let mut schema = if let Some((_, schema_bytes)) = schema_bytes {\n                if let Ok(schema) = serde_json::from_slice::<Schema>(schema_bytes) {\n                    Ok(schema)\n                } else {\n                    schema_error(\"Could not deserialize schema JSON\")\n                }\n            } else {\n                Schema::new(vec![])\n            }?;\n            info_cursor.delete_current()?;\n\n            for col in &mut schema.collections {\n                col.version = version_num as u8;\n                Self::save_schema(info_cursor, col)?;\n            }\n        }\n        Ok(())\n    }\n\n    fn get_schemas(info_cursor: &mut Cursor) -> Result<Vec<CollectionSchema>> {\n        let mut schemas = vec![];\n        info_cursor.iter_all(false, true, |_, _, bytes| {\n            let col = serde_json::from_slice::<CollectionSchema>(bytes).map_err(|_| {\n                IsarError::DbCorrupted {\n                    message: \"Could not deserialize existing schema.\".to_string(),\n                }\n            })?;\n            schemas.push(col);\n            Ok(true)\n        })?;\n        Ok(schemas)\n    }\n\n    fn save_schema(info_cursor: &mut Cursor, schema: &CollectionSchema) -> Result<()> {\n        let key = IndexKey::from_bytes(schema.name.as_bytes().to_vec());\n        let bytes = schema.to_json_bytes()?;\n        info_cursor.put(&key, &bytes)?;\n        Ok(())\n    }\n\n    fn delete_schema(info_cursor: &mut Cursor, schema: &CollectionSchema) -> Result<()> {\n        let key = IndexKey::from_bytes(schema.name.as_bytes().to_vec());\n        if info_cursor.move_to(&key)?.is_some() {\n            info_cursor.delete_current()?;\n        }\n        Ok(())\n    }\n\n    pub fn open_collection_db(txn: &Txn, col: &CollectionSchema) -> Result<Db> {\n        Db::open(txn, Some(&col.name), true, false, false)\n    }\n\n    pub fn open_index_db(txn: &Txn, col: &CollectionSchema, index: &IndexSchema) -> Result<Db> {\n        let db_name = format!(\"_i_{}_{}\", col.name, index.name);\n        Db::open(txn, Some(&db_name), false, !index.unique, false)\n    }\n\n    pub fn open_link_dbs(txn: &Txn, col: &CollectionSchema, link: &LinkSchema) -> Result<(Db, Db)> {\n        let link_db_name = format!(\"_l_{}_{}\", col.name, link.name);\n        let db = Db::open(txn, Some(&link_db_name), true, true, true)?;\n        let backlink_db_name = format!(\"_b_{}_{}\", col.name, link.name);\n        let bl_db = Db::open(txn, Some(&backlink_db_name), true, true, true)?;\n        Ok((db, bl_db))\n    }\n\n    fn delete_collection(txn: &Txn, col: &CollectionSchema) -> Result<()> {\n        let db = Self::open_collection_db(txn, col)?;\n        db.drop(txn)?;\n        for index in &col.indexes {\n            Self::delete_index(txn, col, index)?;\n        }\n        for link in &col.links {\n            Self::delete_link(txn, col, link)?;\n        }\n        Ok(())\n    }\n\n    fn delete_index(txn: &Txn, col: &CollectionSchema, index: &IndexSchema) -> Result<()> {\n        let db = Self::open_index_db(txn, col, index)?;\n        db.drop(txn)\n    }\n\n    fn delete_link(txn: &Txn, col: &CollectionSchema, link: &LinkSchema) -> Result<()> {\n        let (db, bl_db) = Self::open_link_dbs(txn, col, link)?;\n        db.drop(txn)?;\n        bl_db.drop(txn)\n    }\n\n    fn perform_migration(\n        txn: &Txn,\n        schema: &mut CollectionSchema,\n        existing_schema: &CollectionSchema,\n    ) -> Result<Vec<u64>> {\n        let removed_properties = schema.merge_properties(existing_schema)?;\n\n        let mut added_indexes = IntMap::new();\n        for index in &schema.indexes {\n            if !existing_schema.indexes.contains(index) {\n                let index_id = xxh3_64(index.name.as_bytes());\n                added_indexes.insert(index_id, ());\n            }\n        }\n\n        for existing_index in &existing_schema.indexes {\n            let removed_index = !schema.indexes.contains(existing_index);\n            let changed_property = existing_index\n                .properties\n                .iter()\n                .any(|p| removed_properties.contains(&p.name));\n\n            if removed_index || changed_property {\n                Self::delete_index(txn, existing_schema, existing_index)?;\n            }\n\n            if !removed_index && changed_property {\n                let index_id = xxh3_64(existing_index.name.as_bytes());\n                added_indexes.insert(index_id, ());\n            }\n        }\n\n        for link in &existing_schema.links {\n            if !schema.links.contains(link) {\n                Self::delete_link(txn, existing_schema, link)?;\n            }\n        }\n\n        Ok(added_indexes.keys().copied().collect())\n    }\n\n    pub fn migrate_schema(&mut self, txn: &Txn, schemas: &mut Schema) -> Result<IntMap<Vec<u64>>> {\n        let cursors = IsarCursors::new(txn, vec![]);\n\n        let mut added_indexes = IntMap::new();\n        for col_schema in &mut schemas.collections {\n            let mut existing_schema = self\n                .schemas\n                .iter()\n                .position(|s| s.name == col_schema.name)\n                .map(|index| self.schemas.remove(index));\n\n            if let Some(existing_schema) = &mut existing_schema {\n                if existing_schema.version == 1 {\n                    migrate_v1(txn, existing_schema)?;\n                } else if existing_schema.version != Self::ISAR_FILE_VERSION {\n                    return Err(IsarError::VersionError {});\n                }\n                let ai = Self::perform_migration(txn, col_schema, existing_schema)?;\n                if !ai.is_empty() {\n                    added_indexes.insert(xxh3_64(col_schema.name.as_bytes()), ai);\n                }\n            }\n\n            let mut info_cursor = cursors.get_cursor(self.info_db)?;\n            col_schema.version = Self::ISAR_FILE_VERSION;\n            Self::save_schema(&mut info_cursor, &col_schema)?;\n        }\n        Ok(added_indexes)\n    }\n\n    pub fn open_collection(\n        &mut self,\n        txn: &Txn,\n        schema: &CollectionSchema,\n        schemas: &Schema,\n        added_indexes: &[u64],\n    ) -> Result<IsarCollection> {\n        let cursors = IsarCursors::new(txn, vec![]);\n\n        let db = Self::open_collection_db(txn, &schema)?;\n        let properties = schema.get_properties();\n\n        let mut embedded_properties = IntMap::new();\n        Self::get_embedded_properties(schemas, &properties, &mut embedded_properties);\n\n        let indexes = Self::open_indexes(txn, &schema, &properties)?;\n        let links = Self::open_links(txn, db, &schema, schemas)?;\n        let backlinks = Self::open_backlinks(txn, db, &schema, schemas)?;\n        let col = IsarCollection::new(\n            db,\n            self.instance_id,\n            &schema.name,\n            properties,\n            embedded_properties,\n            indexes,\n            links,\n            backlinks,\n        );\n\n        col.init_auto_increment(&cursors)?;\n        if !added_indexes.is_empty() {\n            col.fill_indexes(&added_indexes, &cursors)?;\n        }\n\n        Ok(col)\n    }\n\n    fn get_embedded_properties(\n        schemas: &Schema,\n        properties: &[Property],\n        embedded_properties: &mut IntMap<Vec<Property>>,\n    ) {\n        for property in properties {\n            if let Some(target_id) = property.target_id {\n                if !embedded_properties.contains_key(target_id) {\n                    let embedded_col_schema = schemas\n                        .collections\n                        .iter()\n                        .find(|c| xxh3_64(c.name.as_bytes()) == target_id)\n                        .unwrap();\n                    let properties = embedded_col_schema.get_properties();\n                    embedded_properties.insert(target_id, properties.clone());\n                    Self::get_embedded_properties(schemas, &properties, embedded_properties)\n                }\n            }\n        }\n    }\n\n    fn open_indexes(\n        txn: &Txn,\n        schema: &CollectionSchema,\n        properties: &[Property],\n    ) -> Result<Vec<IsarIndex>> {\n        let mut indexes = vec![];\n        for index_schema in &schema.indexes {\n            let db = Self::open_index_db(txn, schema, index_schema)?;\n            let index = index_schema.as_index(db, &properties);\n            indexes.push(index);\n        }\n        Ok(indexes)\n    }\n\n    fn open_links(\n        txn: &Txn,\n        db: Db,\n        schema: &CollectionSchema,\n        schemas: &Schema,\n    ) -> Result<Vec<IsarLink>> {\n        let mut links = vec![];\n        for link_schema in &schema.links {\n            let (link_db, backlink_db) = Self::open_link_dbs(txn, schema, link_schema)?;\n            let target_col_schema = schemas\n                .get_collection(&link_schema.target_col, false)\n                .unwrap();\n            let target_db = Self::open_collection_db(txn, target_col_schema)?;\n            let link = IsarLink::new(\n                &schema.name,\n                &link_schema.name,\n                false,\n                link_db,\n                backlink_db,\n                db,\n                target_db,\n            );\n            links.push(link);\n        }\n        Ok(links)\n    }\n\n    fn open_backlinks(\n        txn: &Txn,\n        db: Db,\n        schema: &CollectionSchema,\n        schemas: &Schema,\n    ) -> Result<Vec<IsarLink>> {\n        let mut backlinks = vec![];\n        for other_col_schema in &schemas.collections {\n            for link_schema in &other_col_schema.links {\n                if link_schema.target_col == schema.name {\n                    let other_col_db = Self::open_collection_db(txn, other_col_schema)?;\n                    let (link_db, bl_db) = Self::open_link_dbs(txn, other_col_schema, link_schema)?;\n                    let backlink = IsarLink::new(\n                        &other_col_schema.name,\n                        &link_schema.name,\n                        true,\n                        bl_db,\n                        link_db,\n                        db,\n                        other_col_db,\n                    );\n                    backlinks.push(backlink);\n                }\n            }\n        }\n        Ok(backlinks)\n    }\n\n    pub fn delete_unopened_collections(&self, txn: &Txn) -> Result<()> {\n        let mut info_cursor = UnboundCursor::new().bind(txn, self.info_db)?;\n        for col in &self.schemas {\n            Self::delete_collection(txn, col)?;\n            Self::delete_schema(&mut info_cursor, col)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/txn.rs",
    "content": "use crate::cursor::IsarCursors;\nuse crate::error::{IsarError, Result};\nuse crate::mdbx::cursor::UnboundCursor;\nuse crate::mdbx::db::Db;\nuse crate::mdbx::txn::Txn;\nuse crate::watch::change_set::ChangeSet;\nuse std::cell::RefCell;\n\npub struct IsarTxn<'env> {\n    instance_id: u64,\n    txn: Txn<'env>,\n    write: bool,\n    change_set: RefCell<Option<ChangeSet<'env>>>,\n    unbound_cursors: RefCell<Option<Vec<UnboundCursor>>>,\n}\n\nimpl<'env> IsarTxn<'env> {\n    pub(crate) fn new(\n        instance_id: u64,\n        txn: Txn<'env>,\n        write: bool,\n        change_set: Option<ChangeSet<'env>>,\n    ) -> Result<Self> {\n        Ok(IsarTxn {\n            instance_id,\n            txn,\n            write,\n            change_set: RefCell::new(change_set),\n            unbound_cursors: RefCell::new(Some(vec![])),\n        })\n    }\n\n    pub fn is_active(&self) -> bool {\n        self.unbound_cursors.borrow().is_some()\n    }\n\n    fn verify_instance_id(&self, instance_id: u64) -> Result<()> {\n        if self.instance_id != instance_id {\n            Err(IsarError::InstanceMismatch {})\n        } else {\n            Ok(())\n        }\n    }\n\n    pub(crate) fn read<'txn, T, F>(&'txn mut self, instance_id: u64, job: F) -> Result<T>\n    where\n        F: FnOnce(&IsarCursors<'txn, 'env>) -> Result<T>,\n    {\n        self.verify_instance_id(instance_id)?;\n        if let Some(unbound_cursors) = self.unbound_cursors.take() {\n            let cursors = IsarCursors::new(&self.txn, unbound_cursors);\n            let result = job(&cursors);\n            self.unbound_cursors.borrow_mut().replace(cursors.close());\n            result\n        } else {\n            Err(IsarError::TransactionClosed {})\n        }\n    }\n\n    pub(crate) fn write<'txn, T, F>(&'txn mut self, instance_id: u64, job: F) -> Result<T>\n    where\n        F: FnOnce(&IsarCursors<'txn, 'env>, Option<&mut ChangeSet<'_>>) -> Result<T>,\n    {\n        self.verify_instance_id(instance_id)?;\n        if !self.write {\n            return Err(IsarError::WriteTxnRequired {});\n        }\n        if let Some(unbound_cursors) = self.unbound_cursors.take() {\n            let mut change_set = self.change_set.take();\n            let cursors = IsarCursors::new(&self.txn, unbound_cursors);\n            let result = job(&cursors, change_set.as_mut());\n            let unbounded_cursors = cursors.close();\n            if result.is_ok() {\n                self.unbound_cursors.borrow_mut().replace(unbounded_cursors);\n                if let Some(change_set) = change_set {\n                    self.change_set.borrow_mut().replace(change_set);\n                }\n            }\n            result\n        } else {\n            Err(IsarError::TransactionClosed {})\n        }\n    }\n\n    pub fn commit(self) -> Result<()> {\n        if !self.is_active() {\n            return Err(IsarError::TransactionClosed {});\n        }\n\n        if self.write {\n            self.txn.commit()?;\n            if let Some(change_set) = self.change_set.take() {\n                change_set.notify_watchers();\n            }\n        }\n        Ok(())\n    }\n\n    pub fn abort(self) {\n        self.txn.abort()\n    }\n\n    pub(crate) fn db_names(&mut self) -> Result<Vec<String>> {\n        let unnamed_db = Db::open(&self.txn, None, false, false, false)?;\n        let cursor = UnboundCursor::new();\n        let mut cursor = cursor.bind(&self.txn, unnamed_db)?;\n\n        let mut names = vec![];\n        cursor.iter_all(false, true, |_, name, _| {\n            names.push(String::from_utf8(name.to_vec()).unwrap());\n            Ok(true)\n        })?;\n        Ok(names)\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/watch/change_set.rs",
    "content": "use crate::object::isar_object::IsarObject;\nuse crate::watch::isar_watchers::IsarWatchers;\nuse crate::watch::watcher::Watcher;\nuse intmap::IntMap;\nuse std::sync::{Arc, MutexGuard};\n\npub(crate) struct ChangeSet<'a> {\n    watchers: MutexGuard<'a, IsarWatchers>,\n    changed_watchers: IntMap<Arc<Watcher>>,\n}\n\nimpl<'a> ChangeSet<'a> {\n    pub fn new(watchers: MutexGuard<'a, IsarWatchers>) -> Self {\n        ChangeSet {\n            watchers,\n            changed_watchers: IntMap::new(),\n        }\n    }\n\n    fn register_watchers(changed_watchers: &mut IntMap<Arc<Watcher>>, watchers: &[Arc<Watcher>]) {\n        for w in watchers {\n            let registered = changed_watchers.contains_key(w.get_id());\n            if !registered {\n                changed_watchers.insert(w.get_id(), w.clone());\n            } else {\n                break;\n            }\n        }\n    }\n\n    pub fn register_change(&mut self, col_id: u64, id: i64, object: IsarObject) {\n        let cw = self.watchers.get_col_watchers(col_id);\n        Self::register_watchers(&mut self.changed_watchers, &cw.watchers);\n        if let Some(object_watchers) = cw.object_watchers.get(id as u64) {\n            Self::register_watchers(&mut self.changed_watchers, object_watchers);\n        }\n\n        for (q, w) in &cw.query_watchers {\n            if !self.changed_watchers.contains_key(w.get_id())\n                && q.maybe_matches_wc_filter(id, object)\n            {\n                self.changed_watchers.insert(w.get_id(), w.clone());\n            }\n        }\n    }\n\n    pub fn register_all(&mut self, col_id: u64) {\n        let cw = self.watchers.get_col_watchers(col_id);\n        Self::register_watchers(&mut self.changed_watchers, &cw.watchers);\n        for watchers in cw.object_watchers.values() {\n            Self::register_watchers(&mut self.changed_watchers, watchers)\n        }\n        for (_, w) in &cw.query_watchers {\n            self.changed_watchers.insert(w.get_id(), w.clone());\n        }\n    }\n\n    pub fn notify_watchers(self) {\n        for watcher in self.changed_watchers.values() {\n            watcher.notify();\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/watch/isar_watchers.rs",
    "content": "use crate::query::Query;\nuse crate::watch::watcher::{Watcher, WatcherCallback};\nuse crossbeam_channel::Receiver;\nuse intmap::IntMap;\nuse itertools::Itertools;\nuse std::sync::Arc;\n\npub(crate) type WatcherModifier = Box<dyn FnOnce(&mut IsarWatchers) + Send + 'static>;\n\npub(crate) struct IsarWatchers {\n    modifiers: Receiver<WatcherModifier>,\n    collection_watchers: IntMap<IsarCollectionWatchers>,\n}\n\nimpl IsarWatchers {\n    pub fn new(modifiers: Receiver<WatcherModifier>) -> Self {\n        IsarWatchers {\n            modifiers,\n            collection_watchers: IntMap::new(),\n        }\n    }\n\n    pub(crate) fn get_col_watchers(&mut self, col_id: u64) -> &mut IsarCollectionWatchers {\n        if !self.collection_watchers.contains_key(col_id) {\n            self.collection_watchers\n                .insert(col_id, IsarCollectionWatchers::new());\n        }\n        self.collection_watchers.get_mut(col_id).unwrap()\n    }\n\n    pub(crate) fn sync(&mut self) {\n        let modifiers = self.modifiers.try_iter().collect_vec();\n        for modifier in modifiers {\n            modifier(self)\n        }\n    }\n}\n\npub struct IsarCollectionWatchers {\n    pub(super) watchers: Vec<Arc<Watcher>>,\n    pub(super) object_watchers: IntMap<Vec<Arc<Watcher>>>,\n    pub(super) query_watchers: Vec<(Query, Arc<Watcher>)>,\n}\n\nimpl IsarCollectionWatchers {\n    fn new() -> Self {\n        IsarCollectionWatchers {\n            watchers: Vec::new(),\n            object_watchers: IntMap::new(),\n            query_watchers: Vec::new(),\n        }\n    }\n\n    pub fn add_watcher(&mut self, watcher_id: u64, callback: WatcherCallback) {\n        let watcher = Arc::new(Watcher::new(watcher_id, callback));\n        self.watchers.push(watcher);\n    }\n\n    pub fn remove_watcher(&mut self, watcher_id: u64) {\n        let position = self\n            .watchers\n            .iter()\n            .position(|w| w.get_id() == watcher_id)\n            .unwrap();\n        self.watchers.remove(position);\n    }\n\n    pub fn add_object_watcher(&mut self, watcher_id: u64, id: i64, callback: WatcherCallback) {\n        let watcher = Arc::new(Watcher::new(watcher_id, callback));\n        if let Some(object_watchers) = self.object_watchers.get_mut(id as u64) {\n            object_watchers.push(watcher);\n        } else {\n            self.object_watchers.insert(id as u64, vec![watcher]);\n        }\n    }\n\n    pub fn remove_object_watcher(&mut self, id: i64, watcher_id: u64) {\n        let watchers = self.object_watchers.get_mut(id as u64).unwrap();\n        let position = watchers\n            .iter()\n            .position(|w| w.get_id() == watcher_id)\n            .unwrap();\n        watchers.remove(position);\n    }\n\n    pub fn add_query_watcher(&mut self, watcher_id: u64, query: Query, callback: WatcherCallback) {\n        let watcher = Arc::new(Watcher::new(watcher_id, callback));\n        self.query_watchers.push((query, watcher));\n    }\n\n    pub fn remove_query_watcher(&mut self, watcher_id: u64) {\n        let position = self\n            .query_watchers\n            .iter()\n            .position(|(_, w)| w.get_id() == watcher_id)\n            .unwrap();\n        self.query_watchers.remove(position);\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/watch/mod.rs",
    "content": "pub(crate) mod change_set;\npub(crate) mod isar_watchers;\npub(crate) mod watcher;\n\npub struct WatchHandle {\n    stop_callback: Option<Box<dyn FnOnce()>>,\n}\n\nimpl WatchHandle {\n    pub(crate) fn new(stop_callback: Box<dyn FnOnce()>) -> Self {\n        WatchHandle {\n            stop_callback: Some(stop_callback),\n        }\n    }\n\n    pub fn stop(self) {}\n}\n\nimpl Drop for WatchHandle {\n    fn drop(&mut self) {\n        let callback = self.stop_callback.take().unwrap();\n        callback();\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/src/watch/watcher.rs",
    "content": "pub type WatcherCallback = Box<dyn Fn() + Send + Sync + 'static>;\n\npub(super) struct Watcher {\n    id: u64,\n    callback: WatcherCallback,\n}\n\nimpl Watcher {\n    pub fn new(id: u64, callback: WatcherCallback) -> Self {\n        Watcher { id, callback }\n    }\n\n    pub fn get_id(&self) -> u64 {\n        self.id\n    }\n\n    pub fn notify(&self) {\n        (*self.callback)()\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/tests/test_binary.rs",
    "content": "use std::{collections::HashMap, fs};\n\nuse intmap::IntMap;\nuse isar_core::object::isar_object::IsarObject;\nuse isar_core::object::json_encode_decode::JsonEncodeDecode;\nuse isar_core::object::object_builder::ObjectBuilder;\nuse isar_core::object::{data_type::DataType, property::Property};\nuse isar_core::schema::collection_schema::CollectionSchema;\nuse isar_core::schema::property_schema::PropertySchema;\nuse itertools::Itertools;\nuse serde::{Deserialize, Serialize};\nuse serde_json::{from_str, json, Value};\n\n#[derive(PartialEq, Eq, Serialize, Deserialize, Clone)]\npub struct BinaryTest {\n    pub types: Vec<DataType>,\n    pub values: Vec<Value>,\n    pub bytes: Vec<u8>,\n}\n\nimpl BinaryTest {\n    fn create(data: &[(DataType, Value)]) -> Self {\n        let (types, values): (Vec<_>, Vec<_>) = data.iter().cloned().unzip();\n        let properties = Self::create_properties(&types);\n        let embedded_properties = IntMap::new();\n        let json = Self::create_temp_json(&properties, &values);\n        let mut ob = ObjectBuilder::new(&properties, None);\n        JsonEncodeDecode::decode(&properties, &embedded_properties, &mut ob, &json).unwrap();\n        BinaryTest {\n            types,\n            values,\n            bytes: ob.finish().as_bytes().to_vec(),\n        }\n    }\n\n    fn create_properties(types: &[DataType]) -> Vec<Property> {\n        let prop_schemas = types\n            .iter()\n            .enumerate()\n            .map(|(i, t)| PropertySchema::new(Some(format!(\"{}\", i)), *t, None))\n            .collect();\n        let schema = CollectionSchema::new(\"col\", false, prop_schemas, vec![], vec![]);\n        schema.get_properties()\n    }\n\n    fn create_temp_json(properties: &[Property], values: &[Value]) -> Value {\n        let map: HashMap<String, Value> = properties\n            .iter()\n            .zip(values.iter())\n            .map(|(p, v)| (p.name.clone(), v.clone()))\n            .collect();\n        json!(map)\n    }\n}\n\nfn generate_binary_golden() -> Vec<BinaryTest> {\n    let bool_blocks = (DataType::Bool, vec![json!(null), json!(true), json!(false)]);\n    let byte_blocks = (DataType::Byte, vec![json!(0), json!(123), json!(255)]);\n    let int_blocks = (\n        DataType::Int,\n        vec![\n            json!(null),\n            json!(i32::MIN + 1),\n            json!(0i32),\n            json!(i32::MAX),\n        ],\n    );\n    let float_blocks = (\n        DataType::Float,\n        vec![\n            json!(f32::MIN),\n            json!(-0f32),\n            json!(0f32),\n            json!(core::f32::consts::PI),\n            json!(f32::MAX),\n        ],\n    );\n    let long_blocks = (\n        DataType::Long,\n        vec![\n            json!(null),\n            json!(i64::MIN + 1),\n            json!(0i64),\n            json!(i64::MAX),\n        ],\n    );\n    let double_blocks = (\n        DataType::Double,\n        vec![\n            json!(f64::MIN),\n            json!(-0f64),\n            json!(0f64),\n            json!(core::f64::consts::PI),\n            json!(f64::MAX),\n        ],\n    );\n    let string_blocks = (\n        DataType::String,\n        vec![\n            json!(null),\n            json!(\"\"),\n            json!(\"a\"),\n            json!(\"רוצח עז קטנה\"),\n            json!(\"👱👱🏻👱🏼👱🏽👱🏾👱🏿👨‍❤️‍💋‍👨👩‍👩‍👧‍👦🏳️‍⚧️🇵🇷\"),\n            json!(\"Z̤͔ͧ̑̓ä͖̭̈̇lͮ̒ͫǧ̗͚̚o̙̔ͮ̇͐̇\"),\n        ],\n    );\n    let bool_list_blocks = (\n        DataType::BoolList,\n        vec![\n            json!(null),\n            json!([]),\n            json!([null]),\n            json!([null, null, null]),\n            json!([true]),\n            json!([false]),\n            json!([true, null, false, null]),\n        ],\n    );\n    let byte_list_blocks = (\n        DataType::ByteList,\n        vec![json!([]), json!([255]), json!([0]), json!([255, 0, 0, 255])],\n    );\n    let int_list_blocks = (\n        DataType::IntList,\n        vec![\n            json!(null),\n            json!([]),\n            json!([null]),\n            json!([null, null, null]),\n            json!([12345i32]),\n            json!([null, i32::MIN + 1, null, i32::MAX]),\n        ],\n    );\n    let float_list_blocks = (\n        DataType::FloatList,\n        vec![\n            json!(null),\n            json!([]),\n            json!([-0f32, 0f32]),\n            json!([f32::MIN, std::f32::consts::PI, f32::MAX]),\n        ],\n    );\n    let long_list_blocks = (\n        DataType::LongList,\n        vec![\n            json!(null),\n            json!([]),\n            json!([null]),\n            json!([null, null, null]),\n            json!([-324234643i64]),\n            json!([null, i64::MIN + 1, null, null, i64::MAX]),\n        ],\n    );\n    let double_list_blocks = (\n        DataType::DoubleList,\n        vec![\n            json!(null),\n            json!([]),\n            json!([-0f64, 0f64]),\n            json!([f64::MIN, std::f64::consts::PI, f64::MAX]),\n        ],\n    );\n    let string_list_blocks = (\n        DataType::StringList,\n        vec![\n            json!(null),\n            json!([]),\n            json!([null]),\n            json!([null, null]),\n            json!([null, null, null]),\n            json!([\"\"]),\n            json!([\"\", \"\"]),\n            json!([\"\", \"\", \"\"]),\n            json!([\"\", null]),\n            json!([null, \"\"]),\n            json!([\"\", null, null]),\n            json!([null, \"\", null]),\n            json!([null, null, \"\"]),\n            json!([null, \"\", \"\"]),\n            json!([\"\", null, \"\"]),\n            json!([\"\", \"\", null]),\n            json!([\"a\"]),\n            json!([\"a\", \"ab\"]),\n            json!([\"a\", \"ab\", \"abc\"]),\n            json!([null, \"a\"]),\n            json!([\"a\", null]),\n            json!([null, \"a\"]),\n            json!([\"a\", null, null]),\n            json!([null, \"a\", null]),\n            json!([null, null, \"a\"]),\n            json!([null, \"a\", \"bbb\"]),\n            json!([\"a\", null, \"bbb\"]),\n            json!([\"a\", \"bbb\", null]),\n        ],\n    );\n\n    let mut combinations = vec![];\n\n    let static_blocks = normalize(&[\n        bool_blocks,\n        byte_blocks,\n        int_blocks,\n        float_blocks,\n        long_blocks,\n        double_blocks,\n    ]);\n\n    for case1 in &static_blocks {\n        combinations.push(vec![case1.clone()]);\n        for case2 in &static_blocks {\n            combinations.push(vec![case1.clone(), case2.clone()]);\n            for case3 in &static_blocks {\n                combinations.push(vec![case1.clone(), case2.clone(), case3.clone()]);\n            }\n        }\n    }\n\n    let dynamic_blocks = normalize(&[\n        string_blocks,\n        bool_list_blocks,\n        byte_list_blocks,\n        int_list_blocks,\n        float_list_blocks,\n        long_list_blocks,\n        double_list_blocks,\n    ]);\n\n    for case1 in &dynamic_blocks {\n        combinations.push(vec![case1.clone()]);\n        for case2 in &dynamic_blocks {\n            combinations.push(vec![case1.clone(), case2.clone()]);\n            for case3 in &dynamic_blocks {\n                combinations.push(vec![case1.clone(), case2.clone(), case3.clone()]);\n            }\n        }\n    }\n\n    let string_list_blocks = normalize(&[string_list_blocks]);\n    for case1 in &string_list_blocks {\n        combinations.push(vec![case1.clone()]);\n        for case2 in &string_list_blocks {\n            combinations.push(vec![case1.clone(), case2.clone()]);\n            for case3 in &string_list_blocks {\n                combinations.push(vec![case1.clone(), case2.clone(), case3.clone()]);\n            }\n        }\n    }\n\n    for case1 in &static_blocks {\n        for case2 in &dynamic_blocks {\n            combinations.push(vec![case1.clone(), case2.clone()]);\n            combinations.push(vec![case2.clone(), case1.clone()]);\n        }\n    }\n\n    for case1 in &static_blocks {\n        for case2 in &string_list_blocks {\n            combinations.push(vec![case1.clone(), case2.clone()]);\n            combinations.push(vec![case2.clone(), case1.clone()]);\n        }\n    }\n\n    for case1 in &dynamic_blocks {\n        for case2 in &string_list_blocks {\n            combinations.push(vec![case1.clone(), case2.clone()]);\n            combinations.push(vec![case2.clone(), case1.clone()]);\n        }\n    }\n\n    combinations\n        .into_iter()\n        .map(|cases| BinaryTest::create(&cases))\n        .collect()\n}\n\nfn normalize(blocks: &[(DataType, Vec<Value>)]) -> Vec<(DataType, Value)> {\n    blocks\n        .into_iter()\n        .flat_map(|(t, blocks)| blocks.into_iter().map(move |b| (*t, b.clone())))\n        .collect_vec()\n}\n\n#[allow(dead_code)]\nfn overwrite_binary_golden() {\n    let tests = generate_binary_golden();\n    let json = json!(tests);\n    fs::write(\"tests/binary_golden.json\", json.to_string()).unwrap();\n}\n\n#[test]\nfn test_binary_serialize() {\n    let golden_str = fs::read_to_string(\"tests/binary_golden.json\").unwrap();\n    let golden = from_str::<Vec<BinaryTest>>(&golden_str).unwrap();\n    for test in golden.iter() {\n        let properties = BinaryTest::create_properties(&test.types);\n        let embedded_properties = IntMap::new();\n        let golden_json = BinaryTest::create_temp_json(&properties, &test.values);\n        let mut ob = ObjectBuilder::new(&properties, None);\n        JsonEncodeDecode::decode(&properties, &embedded_properties, &mut ob, &golden_json).unwrap();\n        let bytes = ob.finish().as_bytes();\n        if bytes != test.bytes {\n            assert_eq!(bytes, test.bytes);\n        }\n    }\n}\n\n#[test]\nfn test_binary_parse() {\n    let golden_str = fs::read_to_string(\"tests/binary_golden.json\").unwrap();\n    let golden = from_str::<Vec<BinaryTest>>(&golden_str).unwrap();\n    for test in golden.iter() {\n        let properties = BinaryTest::create_properties(&test.types);\n        let embedded_properties = IntMap::new();\n        let golden_json = BinaryTest::create_temp_json(&properties, &test.values);\n        let object = IsarObject::from_bytes(&test.bytes);\n        let generated_map =\n            JsonEncodeDecode::encode(&properties, &embedded_properties, object, true);\n        let generated_json = json!(generated_map);\n        if generated_json != golden_json {\n            assert_eq!(generated_json, golden_json);\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core/tests/test_hash.rs",
    "content": "use xxhash_rust::xxh3::xxh3_64;\n\nconst PRIME32: u64 = 2654435761;\nconst PRIME64: u64 = 11400714785074694797;\n\n#[test]\nfn test_xxh3() {\n    fn generate_test_data(len: u64) -> Vec<u8> {\n        let mut byte_gen = PRIME32;\n\n        let mut buffer = Vec::new();\n        for _ in 0..len {\n            buffer.push((byte_gen >> 56) as u8);\n            let (b, _) = byte_gen.overflowing_mul(PRIME64);\n            byte_gen = b;\n        }\n\n        buffer\n    }\n\n    let data = vec![\n        (0u64, 0x2D06800538D394C2u64), /* empty string */\n        (1, 0xC44BDFF4074EECDB),       /*  1 -  3 */\n        (6, 0x27B56A84CD2D7325),       /*  4 -  8 */\n        (12, 0xA713DAF0DFBB77E7),      /*  9 - 16 */\n        (24, 0xA3FE70BF9D3510EB),      /* 17 - 32 */\n        (48, 0x397DA259ECBA1F11),      /* 33 - 64 */\n        (80, 0xBCDEFBBB2C47C90A),      /* 65 - 96 */\n        (195, 0xCD94217EE362EC3A),     /* 129-240 */\n        (403, 0xCDEB804D65C6DEA4),     /* one block, last stripe is overlapping */\n        (512, 0x617E49599013CB6B),     /* one block, finishing at stripe boundary */\n        (2048, 0xDD59E2C3A5F038E0),    /* 2 blocks, finishing at block boundary */\n        (2240, 0x6E73A90539CF2948),    /* 3 blocks, finishing at stripe boundary */\n        (2367, 0xCB37AEB9E5D361ED),    /* 3 blocks, last stripe is overlapping */\n    ];\n    for (len, output) in data {\n        let input = generate_test_data(len);\n        assert_eq!(xxh3_64(&input), output);\n    }\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/.cargo/config.toml",
    "content": "[target.aarch64-linux-android]\nrustflags = [\"-C\", \"link-arg=-Wl,--hash-style=both\"]\n\n[target.armv7-linux-androideabi]\nrustflags = [\"-C\", \"link-arg=-Wl,--hash-style=both\"]\n\n[target.x86_64-linux-android]\nrustflags = [\"-C\", \"link-arg=-Wl,--hash-style=both\"]\n\n[target.i686-linux-android]\nrustflags = [\"-C\", \"link-arg=-Wl,--hash-style=both\"]\n"
  },
  {
    "path": "packages/isar_core_ffi/Cargo.toml",
    "content": "[package]\nname = \"isar\"\nversion = \"0.0.0\"\nauthors = [\"Simon Leier <simonleier@gmail.com>\"]\nedition = \"2021\"\n\n[dependencies]\nisar-core = { path = \"../isar_core\" }\nthreadpool = \"1.8.1\"\nonce_cell = \"1.10.0\"\nserde_json = \"1.0\"\npaste = \"1.0\"\nunicode-segmentation = \"1.9.0\"\nintmap = \"2.0.0\"\nitertools = \"0.10.3\"\n\n[target.'cfg(any(target_os = \"ios\", target_os = \"macos\"))'.dependencies]\nobjc = \"0.2.7\"\nobjc-foundation = \"0.1.1\"\n\n[target.'cfg(target_os = \"android\")'.dependencies]\njni = \"0.20.0\"\nndk-context = \"0.1.1\"\nonce_cell = \"1.10.0\"\n\n[target.'cfg(not(any(target_os = \"ios\", target_os = \"macos\", target_os = \"android\")))'.dependencies]\ndirs = \"4.0.0\"\n\n[lib]\ncrate-type=[\"staticlib\", \"cdylib\"]\n"
  },
  {
    "path": "packages/isar_core_ffi/build.rs",
    "content": "use std::{env, fs::File, io::Write, path::Path};\n\nfn main() {\n    let out_dir = env::var(\"OUT_DIR\").unwrap();\n    let dest_path = Path::new(&out_dir).join(\"version.rs\");\n    let mut f = File::create(&dest_path).unwrap();\n\n    let version = option_env!(\"ISAR_VERSION\");\n    let version = version.map_or(\"debug\", |version| {\n        let version = if version.starts_with(\"v\") {\n            &version[1..]\n        } else {\n            version\n        };\n        version\n    });\n\n    write!(&mut f, \"const ISAR_VERSION: &str = \\\"{version}\\0\\\";\").unwrap();\n    println!(\"cargo:rerun-if-env-changed=ISAR_VERSION\");\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/c_object_set.rs",
    "content": "use isar_core::object::isar_object::IsarObject;\nuse std::{ptr, slice};\n\n#[repr(C)]\npub struct CObject {\n    id: i64,\n    buffer: *mut u8,\n    buffer_length: u32,\n}\n\nunsafe impl Send for CObject {}\n\nimpl CObject {\n    pub fn new() -> Self {\n        CObject {\n            id: i64::MIN,\n            buffer: std::ptr::null_mut(),\n            buffer_length: 0,\n        }\n    }\n\n    #[allow(clippy::mut_from_ref)]\n    pub fn get_object(&self) -> IsarObject {\n        let bytes = unsafe { slice::from_raw_parts(self.buffer, self.buffer_length as usize) };\n        IsarObject::from_bytes(bytes)\n    }\n\n    pub fn get_id(&mut self) -> i64 {\n        self.id\n    }\n\n    pub fn set_id(&mut self, id: i64) {\n        self.id = id;\n    }\n\n    pub fn set_object(&mut self, object: Option<IsarObject>) {\n        if let Some(object) = object {\n            let bytes = object.as_bytes();\n            let buffer_length = bytes.len() as u32;\n            let buffer = bytes as *const _ as *mut u8;\n            self.buffer = buffer;\n            self.buffer_length = buffer_length;\n        } else {\n            self.buffer = ptr::null_mut();\n            self.buffer_length = 0;\n        }\n    }\n}\n\n#[repr(C)]\npub struct CObjectSet {\n    objects: *mut CObject,\n    length: u32,\n}\n\nunsafe impl Send for CObjectSet {}\n\nimpl CObjectSet {\n    pub fn fill_from_vec(&mut self, objects: Vec<CObject>) {\n        let mut objects = objects.into_boxed_slice();\n        self.objects = objects.as_mut_ptr();\n        self.length = objects.len() as u32;\n        std::mem::forget(objects);\n    }\n\n    #[allow(clippy::mut_from_ref)]\n    pub unsafe fn get_objects(&self) -> &mut [CObject] {\n        std::slice::from_raw_parts_mut(self.objects, self.length as usize)\n    }\n\n    pub fn get_length(&self) -> usize {\n        self.length as usize\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_free_c_object_set(ros: &mut CObjectSet) {\n    Vec::from_raw_parts(ros.objects, ros.length as usize, ros.length as usize);\n    ros.objects = ptr::null_mut();\n    ros.length = 0;\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/crud.rs",
    "content": "use crate::c_object_set::{CObject, CObjectSet};\nuse crate::txn::CIsarTxn;\nuse crate::{from_c_str, BoolSend, UintSend};\nuse intmap::IntMap;\nuse isar_core::collection::IsarCollection;\nuse isar_core::error::IsarError;\nuse isar_core::index::index_key::IndexKey;\nuse serde_json::Value;\nuse std::os::raw::c_char;\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    object: &'static mut CObject,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        let id = object.get_id();\n        let result = collection.get(txn, id)?;\n        object.set_object(result);\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get_by_index(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    index_id: u64,\n    key: *mut IndexKey,\n    object: &'static mut CObject,\n) -> i64 {\n    let key = *Box::from_raw(key);\n    isar_try_txn!(txn, move |txn| {\n        let result = collection.get_by_index(txn, index_id, &key)?;\n        if let Some((id, obj)) = result {\n            object.set_id(id);\n            object.set_object(Some(obj));\n        } else {\n            object.set_object(None);\n        }\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get_all(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    objects: &'static mut CObjectSet,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        for object in objects.get_objects() {\n            let id = object.get_id();\n            let result = collection.get(txn, id)?;\n            object.set_object(result);\n        }\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get_all_by_index(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    index_id: u64,\n    keys: *const *mut IndexKey,\n    objects: &'static mut CObjectSet,\n) -> i64 {\n    let slice = std::slice::from_raw_parts(keys, objects.get_length());\n    let keys: Vec<IndexKey> = slice.iter().map(|k| *Box::from_raw(*k)).collect();\n    isar_try_txn!(txn, move |txn| {\n        for (object, key) in objects.get_objects().iter_mut().zip(keys) {\n            let result = collection.get_by_index(txn, index_id, &key)?;\n            if let Some((id, obj)) = result {\n                object.set_id(id);\n                object.set_object(Some(obj));\n            } else {\n                object.set_object(None);\n            }\n        }\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_put(\n    collection: &'static mut IsarCollection,\n    txn: &mut CIsarTxn,\n    object: &'static mut CObject,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        let id = if object.get_id() != i64::MIN {\n            Some(object.get_id())\n        } else {\n            None\n        };\n        let id = collection.put(txn, id, object.get_object())?;\n        object.set_id(id);\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_put_by_index(\n    collection: &'static mut IsarCollection,\n    txn: &mut CIsarTxn,\n    index_id: u64,\n    object: &'static mut CObject,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        let id = collection.put_by_index(txn, index_id, object.get_object())?;\n        object.set_id(id);\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_put_all(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    objects: &'static mut CObjectSet,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        for object in objects.get_objects() {\n            let id = if object.get_id() != i64::MIN {\n                Some(object.get_id())\n            } else {\n                None\n            };\n            let id = collection.put(txn, id, object.get_object())?;\n            object.set_id(id)\n        }\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_put_all_by_index(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    index_id: u64,\n    objects: &'static mut CObjectSet,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        for object in objects.get_objects() {\n            let id = collection.put_by_index(txn, index_id, object.get_object())?;\n            object.set_id(id)\n        }\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_delete(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    id: i64,\n    deleted: &'static mut bool,\n) -> i64 {\n    let deleted = BoolSend(deleted);\n    isar_try_txn!(txn, move |txn| {\n        *deleted.0 = collection.delete(txn, id)?;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_delete_by_index(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    index_id: u64,\n    key: *mut IndexKey,\n    deleted: &'static mut bool,\n) -> i64 {\n    let deleted = BoolSend(deleted);\n    let key = *Box::from_raw(key);\n    isar_try_txn!(txn, move |txn| {\n        *deleted.0 = collection.delete_by_index(txn, index_id, &key)?;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_delete_all(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    ids: *const i64,\n    ids_length: u32,\n    count: &'static mut u32,\n) -> i64 {\n    let ids = std::slice::from_raw_parts(ids, ids_length as usize);\n    let count = UintSend(count);\n    isar_try_txn!(txn, move |txn| {\n        let mut n = 0u32;\n        for id in ids {\n            if collection.delete(txn, *id)? {\n                n += 1;\n            }\n        }\n        *count.0 = n;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_delete_all_by_index(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    index_id: u64,\n    keys: *const *mut IndexKey,\n    keys_length: u32,\n    count: &'static mut u32,\n) -> i64 {\n    let slice = std::slice::from_raw_parts(keys, keys_length as usize);\n    let keys: Vec<IndexKey> = slice.iter().map(|k| *Box::from_raw(*k)).collect();\n    let count = UintSend(count);\n    isar_try_txn!(txn, move |txn| {\n        let mut n = 0u32;\n        for key in keys {\n            if collection.delete_by_index(txn, index_id, &key)? {\n                n += 1;\n            }\n        }\n        *count.0 = n;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_clear(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| collection.clear(txn))\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_json_import(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    id_name: *const c_char,\n    json_bytes: *const u8,\n    json_length: u32,\n) -> i64 {\n    let id_name = from_c_str(id_name).unwrap();\n    let bytes = std::slice::from_raw_parts(json_bytes, json_length as usize);\n    isar_try_txn!(txn, move |txn| {\n        let json: Value = serde_json::from_slice(bytes).map_err(|_| IsarError::InvalidJson {})?;\n        collection.import_json(txn, id_name, json)\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_count(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    count: &'static mut i64,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        *count = collection.count(txn)? as i64;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get_size(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    include_indexes: bool,\n    include_links: bool,\n    size: &'static mut i64,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        *size = collection.get_size(txn, include_indexes, include_links)? as i64;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_verify(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    objects: &'static mut CObjectSet,\n) -> i64 {\n    let mut objects_map = IntMap::new();\n    for object in objects.get_objects() {\n        objects_map.insert(object.get_id() as u64, object.get_object());\n    }\n    isar_try_txn!(txn, move |txn| { collection.verify(txn, &objects_map) })\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/dart.rs",
    "content": "use once_cell::sync::OnceCell;\n\nstatic DART_POST_C_OBJECT: OnceCell<DartPostCObjectFnType> = OnceCell::new();\n\npub fn dart_post_int(port: DartPort, value: i64) {\n    let dart_post = DART_POST_C_OBJECT.get().unwrap();\n    dart_post(port, &mut DartCObject::new(value));\n}\n\npub type DartPort = i64;\n\npub type DartPostCObjectFnType = extern \"C\" fn(port_id: DartPort, message: *mut DartCObject) -> i8;\n\n#[repr(C)]\npub struct DartCObject {\n    ty: i32,\n    value: DartCObjectValue,\n}\n\nimpl DartCObject {\n    fn new(value: i64) -> Self {\n        DartCObject {\n            ty: 3,\n            value: DartCObjectValue { value },\n        }\n    }\n}\n\n#[repr(C)]\nunion DartCObjectValue {\n    pub value: i64,\n    _union_align: [u64; 5usize],\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_connect_dart_api(ptr: DartPostCObjectFnType) {\n    let _ = DART_POST_C_OBJECT.set(ptr);\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/error.rs",
    "content": "use isar_core::error::Result;\nuse once_cell::sync::Lazy;\nuse std::ffi::CString;\nuse std::os::raw::c_char;\nuse std::sync::Mutex;\n\ntype ErrCounter = (Vec<(i64, String)>, i64);\nstatic ERRORS: Lazy<Mutex<ErrCounter>> = Lazy::new(|| Mutex::new((vec![], 1)));\n\npub trait DartErrCode {\n    fn into_dart_result_code(self) -> i64;\n}\n\nimpl DartErrCode for Result<()> {\n    fn into_dart_result_code(self) -> i64 {\n        if let Err(err) = self {\n            let mut lock = ERRORS.lock().unwrap();\n            let (errors, counter) = &mut (*lock);\n            if errors.len() > 10 {\n                errors.remove(0);\n            }\n            let err_code = *counter;\n            errors.push((err_code, err.to_string()));\n            *counter = counter.wrapping_add(1);\n            if *counter == 0 {\n                *counter = 1\n            }\n            err_code\n        } else {\n            0\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! isar_try {\n    { $($token:tt)* } => {{\n        use crate::error::DartErrCode;\n        #[allow(unused_mut)] {\n            let mut l = || -> isar_core::error::Result<()> {\n                $($token)*\n                Ok(())\n            };\n            l().into_dart_result_code()\n        }\n    }}\n}\n\n#[macro_export]\nmacro_rules! isar_try_txn {\n    { $txn:expr, $closure:expr } => {\n        isar_try! {\n            $txn.exec(Box::new($closure))?;\n        }\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get_error(err_code: i64) -> *mut c_char {\n    let lock = ERRORS.lock().unwrap();\n    let error = lock.0.iter().find(|(code, _)| *code == err_code);\n    if let Some((_, err_msg)) = error {\n        CString::new(err_msg.as_str()).unwrap().into_raw()\n    } else {\n        std::ptr::null_mut()\n    }\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/filter.rs",
    "content": "use crate::from_c_str;\nuse isar_core::collection::IsarCollection;\nuse isar_core::error::illegal_arg;\nuse isar_core::error::Result;\nuse isar_core::object::data_type::DataType;\nuse isar_core::object::property::Property;\nuse isar_core::query::filter::*;\nuse std::os::raw::c_char;\nuse std::slice;\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_static(filter: *mut *const Filter, value: bool) {\n    let query_filter = Filter::stat(value);\n    let ptr = Box::into_raw(Box::new(query_filter));\n    filter.write(ptr);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_and_or_xor(\n    filter: *mut *const Filter,\n    and: bool,\n    exclusive: bool,\n    conditions: *mut *mut Filter,\n    length: u32,\n) {\n    let filters = slice::from_raw_parts(conditions, length as usize)\n        .iter()\n        .map(|f| *Box::from_raw(*f))\n        .collect();\n    let and_or = if and {\n        Filter::and(filters)\n    } else if exclusive {\n        Filter::xor(filters)\n    } else {\n        Filter::or(filters)\n    };\n    let ptr = Box::into_raw(Box::new(and_or));\n    filter.write(ptr);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_not(filter: *mut *const Filter, condition: *mut Filter) {\n    let condition = *Box::from_raw(condition);\n    let not = Filter::not(condition);\n    let ptr = Box::into_raw(Box::new(not));\n    filter.write(ptr);\n}\n\npub fn get_property(\n    collection: &IsarCollection,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> Result<&Property> {\n    let properties = if embedded_col_id != 0 {\n        if let Some(properties) = collection.embedded_properties.get(embedded_col_id) {\n            properties\n        } else {\n            return illegal_arg(\"Embedded collection does not exist.\");\n        }\n    } else {\n        &collection.properties\n    };\n    if let Some(property) = properties.get(property_id as usize) {\n        Ok(property)\n    } else {\n        illegal_arg(\"Property does not exist.\")\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_object(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    condition: *mut Filter,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> i64 {\n    isar_try! {\n        let property = get_property(collection, embedded_col_id, property_id)?;\n        let condition = if !condition.is_null() {\n            Some(*Box::from_raw(condition))\n        } else {\n            None\n        };\n        let query_filter = Filter::object(property, condition)?;\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_link(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    condition: *mut Filter,\n    link_id: u64,\n) -> i64 {\n    isar_try! {\n        let condition = *Box::from_raw(condition);\n        let query_filter = Filter::link(collection, link_id, condition)?;\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_link_length(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    lower: u32,\n    upper: u32,\n    link_id: u64,\n) -> i64 {\n    isar_try! {\n        let query_filter = Filter::link_length(collection, link_id, lower as usize,upper as usize)?;\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_list_length(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    lower: u32,\n    upper: u32,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> i64 {\n    isar_try! {\n        let property = get_property(collection, embedded_col_id, property_id)?;\n        let query_filter = Filter::list_length(property, lower as usize,upper as usize)?;\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_null(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> i64 {\n    isar_try! {\n        let property = get_property(collection, embedded_col_id, property_id)?;\n        let query_filter = Filter::null(property);\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[macro_export]\nmacro_rules! include_num {\n    ($type:ident, $lower:ident, $include_lower:expr, $upper:ident, $include_upper:expr) => {{\n        let lower = $lower.clamp($type::MIN as i64, $type::MAX as i64) as $type;\n        let lower = if !$include_lower {\n            lower.checked_add(1)\n        } else {\n            Some(lower)\n        };\n\n        let upper = $upper.clamp($type::MIN as i64, $type::MAX as i64) as $type;\n        let upper = if !$include_upper {\n            upper.checked_sub(1)\n        } else {\n            Some(upper)\n        };\n\n        (lower, upper)\n    }};\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_id(\n    filter: *mut *const Filter,\n    lower: i64,\n    include_lower: bool,\n    upper: i64,\n    include_upper: bool,\n) {\n    let query_filter = if let (Some(lower), Some(upper)) =\n        include_num!(i64, lower, include_lower, upper, include_upper)\n    {\n        Filter::id(lower, upper)\n    } else {\n        Filter::stat(false)\n    };\n    let ptr = Box::into_raw(Box::new(query_filter));\n    filter.write(ptr);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_long(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    lower: i64,\n    include_lower: bool,\n    upper: i64,\n    include_upper: bool,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> i64 {\n    isar_try! {\n        let property = get_property(collection, embedded_col_id, property_id)?;\n        let query_filter = if property.data_type == DataType::Byte\n            || property.data_type == DataType::ByteList\n            || property.data_type == DataType::Bool\n            || property.data_type == DataType::BoolList\n        {\n            if let (Some(lower), Some(upper)) =\n                include_num!(u8, lower, include_lower, upper, include_upper)\n            {\n                Filter::byte(property, lower, upper)?\n            } else {\n                Filter::stat(false)\n            }\n        } else if property.data_type == DataType::Int || property.data_type == DataType::IntList {\n            if let (Some(lower), Some(upper)) =\n                include_num!(i32, lower, include_lower, upper, include_upper)\n            {\n                Filter::int(property, lower, upper)?\n            } else {\n                Filter::stat(false)\n            }\n        } else {\n            if let (Some(lower), Some(upper)) =\n                include_num!(i64, lower, include_lower, upper, include_upper)\n            {\n                Filter::long(property, lower, upper)?\n            } else {\n                Filter::stat(false)\n            }\n        };\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_double(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    lower: f64,\n    upper: f64,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> i64 {\n    isar_try! {\n        let property = get_property(collection, embedded_col_id, property_id)?;\n        let query_filter = if property.data_type == DataType::Float || property.data_type == DataType::FloatList {\n            let lower = if lower.is_finite() {\n                lower.clamp(f32::MIN as f64, f32::MAX as f64)\n            } else {\n                lower\n            };\n            let upper = if upper.is_finite() {\n                upper.clamp(f32::MIN as f64, f32::MAX as f64)\n            } else {\n                upper\n            };\n            Filter::float(property, lower as f32, upper as f32)?\n        } else {\n            Filter::double(property, lower, upper)?\n        };\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\nunsafe fn get_lower_str(lower: Option<Vec<u8>>, include_lower: bool) -> Option<Vec<u8>> {\n    if include_lower {\n        lower\n    } else if let Some(mut lower) = lower {\n        if let Some(last) = lower.pop() {\n            if last < 255 {\n                lower.push(last + 1);\n            } else {\n                lower.push(255);\n                lower.push(0);\n            }\n        } else {\n            lower.push(0);\n        }\n        Some(lower)\n    } else {\n        Some(vec![])\n    }\n}\n\nunsafe fn get_upper_str(upper: Option<Vec<u8>>, include_upper: bool) -> Option<Option<Vec<u8>>> {\n    if include_upper {\n        Some(upper)\n    } else if let Some(mut upper) = upper {\n        if upper.is_empty() {\n            Some(None)\n        } else {\n            for i in (upper.len() - 1)..0 {\n                if upper[i] > 0 {\n                    upper[i] -= 1;\n                    return Some(Some(upper));\n                }\n            }\n            Some(Some(vec![]))\n        }\n    } else {\n        // cannot exclude upper limit\n        None\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_filter_string(\n    collection: &IsarCollection,\n    filter: *mut *const Filter,\n    lower: *const c_char,\n    include_lower: bool,\n    upper: *const c_char,\n    include_upper: bool,\n    case_sensitive: bool,\n    embedded_col_id: u64,\n    property_id: u64,\n) -> i64 {\n    isar_try! {\n        let property = get_property(collection, embedded_col_id, property_id)?;\n\n        let lower_bytes = Filter::string_to_bytes(from_c_str(lower)?, case_sensitive);\n        let lower = get_lower_str(lower_bytes, include_lower);\n\n        let upper_bytes = Filter::string_to_bytes(from_c_str(upper)?, case_sensitive);\n        let upper = get_upper_str(upper_bytes, include_upper);\n\n        let query_filter = if let Some(upper) = upper {\n            Filter::byte_string(property, lower, upper, case_sensitive)?\n        } else {\n            Filter::stat(false)\n        };\n        let ptr = Box::into_raw(Box::new(query_filter));\n        filter.write(ptr);\n    }\n}\n\n#[macro_export]\nmacro_rules! filter_string_ffi {\n    ($filter_name:ident, $function_name:ident) => {\n        #[no_mangle]\n        pub unsafe extern \"C\" fn $function_name(\n            collection: &IsarCollection,\n            filter: *mut *const Filter,\n            value: *const c_char,\n            case_sensitive: bool,\n            embedded_col_id: u64,\n            property_id: u64,\n        ) -> i64 {\n            isar_try! {\n                let property = get_property(collection, embedded_col_id, property_id)?;\n                let str = from_c_str(value)?.unwrap();\n                let query_filter = isar_core::query::filter::Filter::$filter_name(property, str, case_sensitive)?;\n                let ptr = Box::into_raw(Box::new(query_filter));\n                filter.write(ptr);\n            }\n        }\n    }\n}\n\nfilter_string_ffi!(string_starts_with, isar_filter_string_starts_with);\nfilter_string_ffi!(string_ends_with, isar_filter_string_ends_with);\nfilter_string_ffi!(string_contains, isar_filter_string_contains);\nfilter_string_ffi!(string_matches, isar_filter_string_matches);\n"
  },
  {
    "path": "packages/isar_core_ffi/src/index_key.rs",
    "content": "use crate::from_c_str;\nuse isar_core::index::index_key::IndexKey;\nuse isar_core::object::isar_object::IsarObject;\nuse paste::paste;\nuse std::os::raw::c_char;\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_key_create(key: *mut *const IndexKey) {\n    let index_key = IndexKey::new();\n    let ptr = Box::into_raw(Box::new(index_key));\n    key.write(ptr);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_key_increase(key: &mut IndexKey) -> bool {\n    key.increase()\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_key_decrease(key: &mut IndexKey) -> bool {\n    key.decrease()\n}\n\n#[no_mangle]\npub extern \"C\" fn isar_key_add_byte(key: &mut IndexKey, value: u8) {\n    key.add_byte(value);\n}\n\n#[no_mangle]\npub extern \"C\" fn isar_key_add_int(key: &mut IndexKey, value: i32) {\n    key.add_int(value);\n}\n\n#[no_mangle]\npub extern \"C\" fn isar_key_add_long(key: &mut IndexKey, value: i64) {\n    key.add_long(value);\n}\n\n#[no_mangle]\npub extern \"C\" fn isar_key_add_float(key: &mut IndexKey, value: f64) {\n    let value = if value.is_finite() {\n        value.clamp(f32::MIN as f64, f32::MAX as f64)\n    } else {\n        value\n    };\n    key.add_float(value as f32);\n}\n\n#[no_mangle]\npub extern \"C\" fn isar_key_add_double(key: &mut IndexKey, value: f64) {\n    key.add_double(value);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_key_add_string(\n    key: &mut IndexKey,\n    value: *const c_char,\n    case_sensitive: bool,\n) {\n    let value = from_c_str(value).unwrap();\n    key.add_string(value, case_sensitive)\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_key_add_string_hash(\n    key: &mut IndexKey,\n    value: *const c_char,\n    case_sensitive: bool,\n) {\n    let value = from_c_str(value).unwrap();\n    let hash = IsarObject::hash_string(value, case_sensitive, 0);\n    key.add_hash(hash);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_key_add_string_list_hash(\n    key: &mut IndexKey,\n    value: *const *const c_char,\n    length: u32,\n    case_sensitive: bool,\n) {\n    let value = if !value.is_null() {\n        let raw_strings = std::slice::from_raw_parts(value, length as usize);\n        let mut strings = vec![];\n        for raw_str in raw_strings {\n            let str = from_c_str(*raw_str).unwrap();\n            strings.push(str);\n        }\n        Some(strings)\n    } else {\n        None\n    };\n    let hash = IsarObject::hash_string_list(value, case_sensitive, 0);\n    key.add_hash(hash);\n}\n\n#[macro_export]\nmacro_rules! hash_list {\n    ($name:ident, $type:ty) => {\n        paste! {\n            #[no_mangle]\n            pub unsafe extern \"C\" fn [<isar_key_add_ $name _list_hash>](\n                key: &mut IndexKey,\n                value: *const $type,\n                length: u32,\n            ) {\n                let value = if !value.is_null() {\n                    Some(std::slice::from_raw_parts(value, length as usize))\n                } else {\n                    None\n                };\n                let hash = IsarObject::hash_list(value, 0);\n                key.add_hash(hash);\n            }\n        }\n    };\n}\n\nhash_list!(byte, u8);\nhash_list!(int, i32);\nhash_list!(long, i64);\n"
  },
  {
    "path": "packages/isar_core_ffi/src/instance.rs",
    "content": "use crate::dart::{dart_post_int, DartPort};\nuse crate::error::DartErrCode;\nuse crate::from_c_str;\nuse crate::txn::run_async;\nuse crate::txn::CIsarTxn;\nuse crate::CharsSend;\nuse isar_core::collection::IsarCollection;\nuse isar_core::error::{illegal_arg, Result};\nuse isar_core::instance::{CompactCondition, IsarInstance};\nuse isar_core::schema::Schema;\nuse std::ffi::CString;\nuse std::os::raw::c_char;\nuse std::sync::Arc;\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/version.rs\"));\n\nstruct IsarInstanceSend(*mut *const IsarInstance);\n\nunsafe impl Send for IsarInstanceSend {}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_version() -> *const c_char {\n    ISAR_VERSION.as_ptr() as *const c_char\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_create(\n    isar: *mut *const IsarInstance,\n    name: *const c_char,\n    path: *const c_char,\n    schema_json: *const c_char,\n    max_size_mib: i64,\n    relaxed_durability: bool,\n    compact_min_file_size: u32,\n    compact_min_bytes: u32,\n    compact_min_ratio: f64,\n) -> i64 {\n    let open = || -> Result<()> {\n        let name = from_c_str(name).unwrap().unwrap();\n        let path = from_c_str(path).unwrap();\n        let schema_json = from_c_str(schema_json).unwrap().unwrap();\n        let schema = Schema::from_json(schema_json.as_bytes())?;\n\n        let compact_condition = if compact_min_ratio.is_nan() {\n            None\n        } else {\n            Some(CompactCondition {\n                min_file_size: compact_min_file_size as u64,\n                min_bytes: compact_min_bytes as u64,\n                min_ratio: compact_min_ratio,\n            })\n        };\n\n        let instance = IsarInstance::open(\n            name,\n            path,\n            schema,\n            max_size_mib as usize,\n            relaxed_durability,\n            compact_condition,\n        )?;\n        isar.write(Arc::into_raw(instance));\n        Ok(())\n    };\n\n    open().into_dart_result_code()\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_create_async(\n    isar: *mut *const IsarInstance,\n    name: *const c_char,\n    path: *const c_char,\n    schema_json: *const c_char,\n    max_size_mib: i64,\n    relaxed_durability: bool,\n    compact_min_file_size: u32,\n    compact_min_bytes: u32,\n    compact_min_ratio: f64,\n    port: DartPort,\n) {\n    let isar = IsarInstanceSend(isar);\n    let name = CharsSend(name);\n    let path = CharsSend(path);\n    let schema_json = CharsSend(schema_json);\n    run_async(move || {\n        let isar = isar;\n        let name = name;\n        let path = path;\n        let schema_json = schema_json;\n        let result = isar_instance_create(\n            isar.0,\n            name.0,\n            path.0,\n            schema_json.0,\n            max_size_mib,\n            relaxed_durability,\n            compact_min_file_size,\n            compact_min_bytes,\n            compact_min_ratio,\n        );\n        dart_post_int(port, result);\n    });\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_close(isar: *const IsarInstance) -> bool {\n    let isar = Arc::from_raw(isar);\n    isar.close()\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_close_and_delete(isar: *const IsarInstance) -> bool {\n    let isar = Arc::from_raw(isar);\n    isar.close_and_delete()\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_get_path(isar: &'static IsarInstance) -> *mut c_char {\n    CString::new(isar.dir.as_str()).unwrap().into_raw()\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_get_collection<'a>(\n    isar: &'a IsarInstance,\n    collection: *mut &'a IsarCollection,\n    collection_id: u64,\n) -> i64 {\n    isar_try! {\n        let new_collection = isar.collections.iter().find(|c| c.id == collection_id);\n        if let Some(new_collection) = new_collection {\n            collection.write(new_collection);\n        } else {\n            illegal_arg(\"Collection id is invalid.\")?;\n        }\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_get_size(\n    instance: &'static IsarInstance,\n    txn: &mut CIsarTxn,\n    include_indexes: bool,\n    include_links: bool,\n    size: &'static mut i64,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        *size = instance.get_size(txn, include_indexes, include_links)? as i64;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_copy_to_file(\n    instance: &'static IsarInstance,\n    path: *const c_char,\n    port: DartPort,\n) {\n    let path = CharsSend(path);\n    run_async(move || {\n        let path = path;\n        let path = from_c_str(path.0).unwrap().unwrap();\n        let result = instance.copy_to_file(path);\n        dart_post_int(port, result.into_dart_result_code());\n    });\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_instance_verify(\n    instance: &'static IsarInstance,\n    txn: &mut CIsarTxn,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| { instance.verify(txn) })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_get_offsets(\n    collection: &IsarCollection,\n    embedded_col_id: u64,\n    offsets: *mut u32,\n) -> u32 {\n    let properties = if embedded_col_id == 0 {\n        &collection.properties\n    } else {\n        collection.embedded_properties.get(embedded_col_id).unwrap()\n    };\n    let offsets = std::slice::from_raw_parts_mut(offsets, properties.len());\n    for (i, p) in properties.iter().enumerate() {\n        offsets[i] = p.offset as u32;\n    }\n    let property = properties.iter().max_by_key(|p| p.offset);\n    property.map_or(2, |p| p.offset + p.data_type.get_static_size()) as u32\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/lib.rs",
    "content": "#![allow(clippy::missing_safety_doc)]\n\nuse isar_core::error::{illegal_arg, Result};\nuse std::ffi::CStr;\nuse std::ffi::CString;\nuse std::mem;\nuse std::os::raw::c_char;\nuse unicode_segmentation::UnicodeSegmentation;\n\n#[macro_use]\nmod error;\n\npub mod c_object_set;\npub mod crud;\nmod dart;\npub mod filter;\npub mod index_key;\npub mod instance;\npub mod link;\npub mod query;\npub mod query_aggregation;\npub mod txn;\npub mod watchers;\n\npub unsafe fn from_c_str<'a>(str: *const c_char) -> Result<Option<&'a str>> {\n    if !str.is_null() {\n        match CStr::from_ptr(str).to_str() {\n            Ok(str) => Ok(Some(str)),\n            Err(_) => illegal_arg(\"The provided String is not valid.\"),\n        }\n    } else {\n        Ok(None)\n    }\n}\n\npub struct UintSend(&'static mut u32);\n\nunsafe impl Send for UintSend {}\n\npub struct BoolSend(&'static mut bool);\n\nunsafe impl Send for BoolSend {}\n\npub struct CharsSend(*const c_char);\n\nunsafe impl Send for CharsSend {}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_find_word_boundaries(\n    input_bytes: *const u8,\n    length: u32,\n    number_words: *mut u32,\n) -> *mut u32 {\n    let bytes = std::slice::from_raw_parts(input_bytes, length as usize);\n    let str = std::str::from_utf8_unchecked(bytes);\n    let mut result = vec![];\n    for (offset, word) in str.unicode_word_indices() {\n        result.push(offset as u32);\n        result.push((offset + word.len()) as u32);\n    }\n    result.shrink_to_fit();\n    number_words.write((result.len() / 2) as u32);\n    let result_ptr = result.as_mut_ptr();\n    mem::forget(result);\n    result_ptr\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_free_word_boundaries(boundaries: *mut u32, word_count: u32) {\n    let len = (word_count * 2) as usize;\n    Vec::from_raw_parts(boundaries, len, len);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_free_string(string: *mut c_char) {\n    let _ = CString::from_raw(string);\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/link.rs",
    "content": "use crate::txn::CIsarTxn;\nuse isar_core::collection::IsarCollection;\nuse isar_core::error::Result;\nuse itertools::Itertools;\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_link(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    link_id: u64,\n    id: i64,\n    target_id: i64,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| -> Result<()> {\n        collection.link(txn, link_id, id, target_id)?;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_link_unlink(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    link_id: u64,\n    id: i64,\n    target_id: i64,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| -> Result<()> {\n        collection.unlink(txn, link_id, id, target_id)?;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_link_unlink_all(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    link_id: u64,\n    id: i64,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| -> Result<()> {\n        collection.unlink_all(txn, link_id, id)?;\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_link_update_all(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    link_id: u64,\n    id: i64,\n    ids: *const i64,\n    link_count: u32,\n    unlink_count: u32,\n    replace: bool,\n) -> i64 {\n    let ids = std::slice::from_raw_parts(ids, (link_count + unlink_count) as usize);\n    isar_try_txn!(txn, move |txn| {\n        if replace {\n            collection.unlink_all(txn, link_id, id)?;\n        }\n        for target_id in ids.iter().take(link_count as usize) {\n            collection.link(txn, link_id, id, *target_id)?;\n        }\n        for target_id in ids\n            .iter()\n            .skip(link_count as usize)\n            .take(unlink_count as usize)\n        {\n            collection.unlink(txn, link_id, id, *target_id)?;\n        }\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_link_verify(\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    link_id: u64,\n    ids: *const i64,\n    ids_count: u32,\n) -> i64 {\n    let ids = std::slice::from_raw_parts(ids, ids_count as usize);\n    let links = ids.iter().copied().tuples().collect_vec();\n    isar_try_txn!(txn, move |txn| -> Result<()> {\n        collection.verify_link(txn, link_id, &links)?;\n        Ok(())\n    })\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/query.rs",
    "content": "use super::c_object_set::{CObject, CObjectSet};\nuse crate::filter::get_property;\nuse crate::txn::CIsarTxn;\nuse crate::{from_c_str, UintSend};\nuse isar_core::collection::IsarCollection;\nuse isar_core::index::index_key::IndexKey;\nuse isar_core::query::filter::Filter;\nuse isar_core::query::query_builder::QueryBuilder;\nuse isar_core::query::{Query, Sort};\nuse std::os::raw::c_char;\n\n#[no_mangle]\npub extern \"C\" fn isar_qb_create(collection: &IsarCollection) -> *mut QueryBuilder {\n    let builder = collection.new_query_builder();\n    Box::into_raw(Box::new(builder))\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_add_id_where_clause(\n    builder: &mut QueryBuilder,\n    start_id: i64,\n    end_id: i64,\n) -> i64 {\n    isar_try! {\n        builder.add_id_where_clause(start_id, end_id)?;\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_add_index_where_clause(\n    builder: &mut QueryBuilder,\n    index_id: u64,\n    lower_key: *mut IndexKey,\n    upper_key: *mut IndexKey,\n    sort_asc: bool,\n    skip_duplicates: bool,\n) -> i64 {\n    let lower_key = *Box::from_raw(lower_key);\n    let upper_key = *Box::from_raw(upper_key);\n    let sort = if sort_asc {\n        Sort::Ascending\n    } else {\n        Sort::Descending\n    };\n    isar_try! {\n        builder.add_index_where_clause(\n            index_id ,\n            lower_key,\n            upper_key,\n            sort,\n            skip_duplicates,\n        )?;\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_add_link_where_clause(\n    builder: &mut QueryBuilder,\n    source_collection: &IsarCollection,\n    link_id: u64,\n    id: i64,\n) -> i64 {\n    isar_try! {\n        builder.add_link_where_clause(source_collection, link_id, id)?;\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_set_filter(builder: &mut QueryBuilder, filter: *mut Filter) {\n    let filter = *Box::from_raw(filter);\n    builder.set_filter(filter);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_add_sort_by(\n    builder: &mut QueryBuilder,\n    property_id: u64,\n    asc: bool,\n) -> i64 {\n    let sort = if asc {\n        Sort::Ascending\n    } else {\n        Sort::Descending\n    };\n    isar_try! {\n        let property = get_property(builder.collection, 0, property_id)?;\n        builder.add_sort(property, sort)?;\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_add_distinct_by(\n    builder: &mut QueryBuilder,\n    property_id: u64,\n    case_sensitive: bool,\n) -> i64 {\n    isar_try! {\n        let property = get_property(builder.collection, 0, property_id)?;\n            builder.add_distinct(property, case_sensitive);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_set_offset_limit(\n    builder: &mut QueryBuilder,\n    offset: i64,\n    limit: i64,\n) {\n    let offset = if offset < 0 { 0 } else { offset as usize };\n    let limit = if limit < 0 {\n        usize::MAX\n    } else {\n        limit as usize\n    };\n    builder.set_offset(offset);\n    builder.set_limit(limit);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_qb_build(builder: *mut QueryBuilder) -> *mut Query {\n    let query = Box::from_raw(builder).build();\n    Box::into_raw(Box::new(query))\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_free(query: *mut Query) {\n    let _ = Box::from_raw(query);\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_find(\n    query: &'static Query,\n    txn: &mut CIsarTxn,\n    result: &'static mut CObjectSet,\n    limit: u32,\n) -> i64 {\n    isar_try_txn!(txn, move |txn| {\n        let mut objects = vec![];\n        let mut count = 0;\n        query.find_while(txn, |id, object| {\n            let mut raw_obj = CObject::new();\n            raw_obj.set_id(id);\n            raw_obj.set_object(Some(object));\n            objects.push(raw_obj);\n            count += 1;\n            count < limit\n        })?;\n\n        result.fill_from_vec(objects);\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_delete(\n    query: &'static Query,\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    limit: u32,\n    count: &'static mut u32,\n) -> i64 {\n    let limit = limit as usize;\n    let count = UintSend(count);\n    isar_try_txn!(txn, move |txn| {\n        let mut ids_to_delete = vec![];\n        query.find_while(txn, |id, _| {\n            ids_to_delete.push(id);\n            ids_to_delete.len() <= limit\n        })?;\n        *count.0 = ids_to_delete.len() as u32;\n        for id in ids_to_delete {\n            collection.delete(txn, id)?;\n        }\n        Ok(())\n    })\n}\n\nstruct JsonBytes(*mut *mut u8);\nunsafe impl Send for JsonBytes {}\n\nstruct JsonLen(*mut u32);\nunsafe impl Send for JsonLen {}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_export_json(\n    query: &'static Query,\n    collection: &'static IsarCollection,\n    txn: &mut CIsarTxn,\n    id_name: *const c_char,\n    json_bytes: *mut *mut u8,\n    json_length: *mut u32,\n) -> i64 {\n    let id_name = from_c_str(id_name).unwrap();\n    let json = JsonBytes(json_bytes);\n    let json_length = JsonLen(json_length);\n    isar_try_txn!(txn, move |txn| {\n        let json = json;\n        let json_length = json_length;\n        let exported_json = query.export_json(txn, collection, id_name, true)?;\n        let bytes = serde_json::to_vec(&exported_json).unwrap();\n        let mut bytes = bytes.into_boxed_slice();\n        json_length.0.write(bytes.len() as u32);\n        json.0.write(bytes.as_mut_ptr());\n        std::mem::forget(bytes);\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_free_json(json_bytes: *mut u8, json_length: u32) {\n    Vec::from_raw_parts(json_bytes, json_length as usize, json_length as usize);\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/query_aggregation.rs",
    "content": "use crate::filter::get_property;\nuse crate::txn::CIsarTxn;\nuse isar_core::collection::IsarCollection;\nuse isar_core::error::Result;\nuse isar_core::object::data_type::DataType;\nuse isar_core::object::isar_object::IsarObject;\nuse isar_core::object::property::Property;\nuse isar_core::query::Query;\nuse isar_core::txn::IsarTxn;\nuse std::cmp::Ordering;\n\npub enum AggregationResult {\n    Long(i64),\n    Double(f64),\n    Null,\n}\n\n#[derive(PartialEq)]\n#[repr(u8)]\npub enum AggregationOp {\n    Min,\n    Max,\n    Sum,\n    Average,\n    Count,\n    IsEmpty,\n}\n\nimpl AggregationOp {\n    fn from_u8(index: u8) -> AggregationOp {\n        match index {\n            0 => AggregationOp::Min,\n            1 => AggregationOp::Max,\n            2 => AggregationOp::Sum,\n            3 => AggregationOp::Average,\n            4 => AggregationOp::Count,\n            5 => AggregationOp::IsEmpty,\n            _ => unreachable!(),\n        }\n    }\n}\n\nconst EMPTY_PROP: &Property = &Property {\n    name: String::new(),\n    data_type: DataType::Bool,\n    offset: 0,\n    target_id: None,\n};\n\nfn aggregate(\n    query: &Query,\n    txn: &mut IsarTxn,\n    op: AggregationOp,\n    property: Option<&Property>,\n) -> Result<AggregationResult> {\n    let mut count = 0usize;\n\n    let (mut long_value, mut double_value) = if op == AggregationOp::Min {\n        (i64::MAX, f64::INFINITY)\n    } else if op == AggregationOp::Max {\n        (i64::MIN, f64::NEG_INFINITY)\n    } else {\n        (0, 0.0)\n    };\n\n    let min_max_cmp = if op == AggregationOp::Max {\n        Ordering::Greater\n    } else {\n        Ordering::Less\n    };\n\n    let property = property.unwrap_or(EMPTY_PROP);\n\n    query.find_while(txn, |_, obj| {\n        match op {\n            AggregationOp::Min | AggregationOp::Max => {\n                if obj.is_null(property.offset, property.data_type) {\n                    return true;\n                }\n\n                count += 1;\n                match property.data_type {\n                    DataType::Int | DataType::Long => {\n                        let value = if property.data_type == DataType::Int {\n                            obj.read_int(property.offset) as i64\n                        } else {\n                            obj.read_long(property.offset)\n                        };\n                        if value.cmp(&long_value) == min_max_cmp {\n                            long_value = value;\n                        }\n                    }\n                    DataType::Float | DataType::Double => {\n                        let value = if property.data_type == DataType::Float {\n                            obj.read_float(property.offset) as f64\n                        } else {\n                            obj.read_double(property.offset)\n                        };\n                        if value.total_cmp(&double_value) == min_max_cmp {\n                            double_value = value;\n                        }\n                    }\n                    _ => {}\n                }\n            }\n            AggregationOp::Sum | AggregationOp::Average => {\n                if obj.is_null(property.offset, property.data_type) {\n                    return true;\n                }\n\n                count += 1;\n                match property.data_type {\n                    DataType::Int => {\n                        long_value = long_value.saturating_add(obj.read_int(property.offset) as i64)\n                    }\n                    DataType::Long => {\n                        long_value = long_value.saturating_add(obj.read_long(property.offset))\n                    }\n                    DataType::Float => double_value += obj.read_float(property.offset) as f64,\n                    DataType::Double => double_value += obj.read_double(property.offset),\n                    _ => {}\n                }\n            }\n            AggregationOp::Count => {\n                count += 1;\n            }\n            AggregationOp::IsEmpty => {\n                count += 1;\n                return false;\n            }\n        }\n        true\n    })?;\n\n    match op {\n        AggregationOp::Min | AggregationOp::Max | AggregationOp::Average => {\n            if count == 0 {\n                return Ok(AggregationResult::Null);\n            }\n        }\n        _ => {}\n    }\n\n    let result = match op {\n        AggregationOp::Average => match property.data_type {\n            DataType::Int | DataType::Long => {\n                AggregationResult::Double((long_value as f64) / (count as f64))\n            }\n            DataType::Float | DataType::Double => {\n                AggregationResult::Double(double_value / (count as f64))\n            }\n            _ => AggregationResult::Null,\n        },\n        AggregationOp::Count => AggregationResult::Long(count as i64),\n        AggregationOp::IsEmpty => AggregationResult::Long(if count > 0 { 0 } else { 1 }),\n        _ => match property.data_type {\n            DataType::Int | DataType::Long => AggregationResult::Long(long_value),\n            DataType::Float | DataType::Double => AggregationResult::Double(double_value),\n            _ => AggregationResult::Null,\n        },\n    };\n\n    Ok(result)\n}\n\npub struct AggregationResultSend(*mut *const AggregationResult);\n\nunsafe impl Send for AggregationResultSend {}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_aggregate(\n    collection: &'static IsarCollection,\n    query: &'static Query,\n    txn: &mut CIsarTxn,\n    operation: u8,\n    property_id: u64,\n    result: *mut *const AggregationResult,\n) -> i64 {\n    let op = AggregationOp::from_u8(operation);\n    let result = AggregationResultSend(result);\n    isar_try_txn!(txn, move |txn| {\n        let result = result;\n        let property = if op != AggregationOp::Count {\n            Some(get_property(collection, 0, property_id)?)\n        } else {\n            None\n        };\n        let aggregate_result = aggregate(query, txn, op, property)?;\n        result.0.write(Box::into_raw(Box::new(aggregate_result)));\n        Ok(())\n    })\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_aggregate_long_result(result: &AggregationResult) -> i64 {\n    match result {\n        AggregationResult::Long(long) => *long,\n        AggregationResult::Double(double) => *double as i64,\n        AggregationResult::Null => IsarObject::NULL_LONG,\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_q_aggregate_double_result(result: &AggregationResult) -> f64 {\n    match result {\n        AggregationResult::Long(long) => *long as f64,\n        AggregationResult::Double(double) => *double,\n        AggregationResult::Null => IsarObject::NULL_DOUBLE,\n    }\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/txn.rs",
    "content": "use crate::dart::{dart_post_int, DartPort};\nuse crate::error::DartErrCode;\nuse isar_core::error::{IsarError, Result};\nuse isar_core::instance::IsarInstance;\nuse isar_core::txn::IsarTxn;\nuse once_cell::sync::Lazy;\nuse std::borrow::BorrowMut;\nuse std::sync::mpsc;\nuse std::sync::mpsc::{Receiver, Sender};\nuse std::sync::Arc;\nuse std::sync::Mutex;\nuse threadpool::{Builder, ThreadPool};\n\nstatic THREAD_POOL: Lazy<Mutex<ThreadPool>> =\n    Lazy::new(|| Mutex::new(Builder::new().thread_name(\"isarworker\".to_string()).build()));\n\npub fn run_async<F: FnOnce() + Send + 'static>(job: F) {\n    THREAD_POOL.lock().unwrap().execute(job);\n}\n\ntype AsyncJob = (Box<dyn FnOnce() + Send + 'static>, bool);\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_txn_begin(\n    isar: &'static IsarInstance,\n    txn: *mut *const CIsarTxn,\n    sync: bool,\n    write: bool,\n    silent: bool,\n    port: DartPort,\n) -> i64 {\n    isar_try! {\n        let new_txn = if sync {\n            CIsarTxn::begin_sync(isar, write, silent)?\n        } else {\n            CIsarTxn::begin_async(isar, write, silent, port)\n        };\n        let txn_ptr = Box::into_raw(Box::new(new_txn));\n        txn.write(txn_ptr);\n    }\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_txn_finish(txn: *mut CIsarTxn, commit: bool) -> i64 {\n    let txn = Box::from_raw(txn);\n    isar_try! {\n        txn.finish(commit)?;\n    }\n}\n\npub struct IsarTxnSend(IsarTxn<'static>);\n\nunsafe impl Send for IsarTxnSend {}\n\npub enum CIsarTxn {\n    Sync {\n        txn: Option<IsarTxn<'static>>,\n    },\n    Async {\n        tx: Sender<AsyncJob>,\n        port: DartPort,\n        txn: Arc<Mutex<Option<IsarTxnSend>>>,\n    },\n}\n\nimpl CIsarTxn {\n    fn begin_sync(isar: &'static IsarInstance, write: bool, silent: bool) -> Result<CIsarTxn> {\n        let sync_txn = CIsarTxn::Sync {\n            txn: Some(isar.begin_txn(write, silent)?),\n        };\n        Ok(sync_txn)\n    }\n\n    fn begin_async(\n        isar: &'static IsarInstance,\n        write: bool,\n        silent: bool,\n        port: DartPort,\n    ) -> CIsarTxn {\n        let (tx, rx): (Sender<AsyncJob>, Receiver<AsyncJob>) = mpsc::channel();\n        let txn = Arc::new(Mutex::new(None));\n        let txn_clone = txn.clone();\n        run_async(move || {\n            let new_txn = isar.begin_txn(write, silent);\n            match new_txn {\n                Ok(new_txn) => {\n                    txn_clone.lock().unwrap().replace(IsarTxnSend(new_txn));\n                    dart_post_int(port, 0);\n                    loop {\n                        let (job, stop) = rx.recv().unwrap();\n                        job();\n                        if stop {\n                            break;\n                        }\n                    }\n                }\n                Err(e) => {\n                    dart_post_int(port, Err(e).into_dart_result_code());\n                }\n            }\n        });\n\n        CIsarTxn::Async { tx, port, txn }\n    }\n\n    pub fn exec_async_internal<F: FnOnce() -> Result<()> + Send + 'static>(\n        job: F,\n        port: DartPort,\n        tx: Sender<AsyncJob>,\n        stop: bool,\n    ) {\n        let handle_response_job = move || {\n            let result = job().into_dart_result_code();\n            dart_post_int(port, result as i64);\n        };\n        tx.send((Box::new(handle_response_job), stop)).unwrap();\n    }\n\n    pub fn exec(\n        &mut self,\n        job: Box<dyn FnOnce(&mut IsarTxn) -> Result<()> + Send + 'static>,\n    ) -> Result<()> {\n        match self.borrow_mut() {\n            CIsarTxn::Sync { ref mut txn } => {\n                if let Some(ref mut txn) = txn {\n                    job(txn)\n                } else {\n                    Err(IsarError::TransactionClosed {})\n                }\n            }\n            CIsarTxn::Async { txn, tx, port } => {\n                let txn = txn.clone();\n                let job = move || -> Result<()> {\n                    let mut lock = txn.lock().unwrap();\n                    if let Some(ref mut txn) = *lock {\n                        job(&mut txn.0)\n                    } else {\n                        Err(IsarError::TransactionClosed {})\n                    }\n                };\n                CIsarTxn::exec_async_internal(job, *port, tx.clone(), false);\n                Ok(())\n            }\n        }\n    }\n\n    pub fn finish(self, commit: bool) -> Result<()> {\n        match self {\n            CIsarTxn::Sync { mut txn } => {\n                if let Some(txn) = txn.take() {\n                    if commit {\n                        txn.commit()\n                    } else {\n                        txn.abort();\n                        Ok(())\n                    }\n                } else {\n                    Err(IsarError::TransactionClosed {})\n                }\n            }\n            CIsarTxn::Async { txn, tx, port } => {\n                let txn = txn.clone();\n                let job = move || -> Result<()> {\n                    let mut lock = txn.lock().unwrap();\n                    if let Some(txn) = (*lock).take() {\n                        if commit {\n                            txn.0.commit()\n                        } else {\n                            txn.0.abort();\n                            Ok(())\n                        }\n                    } else {\n                        Err(IsarError::TransactionClosed {})\n                    }\n                };\n                CIsarTxn::exec_async_internal(job, port, tx.clone(), true);\n                Ok(())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "packages/isar_core_ffi/src/watchers.rs",
    "content": "use isar_core::collection::IsarCollection;\nuse isar_core::instance::IsarInstance;\nuse isar_core::query::Query;\nuse isar_core::watch::WatchHandle;\nuse crate::dart::{dart_post_int, DartPort};\n\n#[no_mangle]\npub extern \"C\" fn isar_watch_collection(\n    isar: &IsarInstance,\n    collection: &IsarCollection,\n    port: DartPort,\n) -> *mut WatchHandle {\n    let handle = isar.watch_collection(\n        collection,\n        Box::new(move ||  {\n            dart_post_int(port, 1);\n        }),\n    );\n    Box::into_raw(Box::new(handle))\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_watch_object(\n    isar: &IsarInstance,\n    collection: &IsarCollection,\n    id: i64,\n    port: DartPort,\n) -> *mut WatchHandle {\n    let handle = isar.watch_object(\n        collection,\n        id,\n        Box::new(move || {\n            dart_post_int(port, 1);\n        }),\n    );\n    Box::into_raw(Box::new(handle))\n}\n\n#[no_mangle]\npub extern \"C\" fn isar_watch_query(\n    isar: &IsarInstance,\n    collection: &IsarCollection,\n    query: &Query,\n    port: DartPort,\n) -> *mut WatchHandle {\n    let handle = isar.watch_query(\n        collection,\n        query.clone(),\n        Box::new(move ||  {\n            dart_post_int(port, 1);\n        }),\n    );\n    Box::into_raw(Box::new(handle))\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn isar_stop_watching(handle: *mut WatchHandle) {\n    Box::from_raw(handle).stop();\n}\n"
  },
  {
    "path": "packages/isar_flutter_libs/.pubignore",
    "content": "!*.so\n!*.a\n!*.dylib\n!*.dll\n!*.xcframework/"
  },
  {
    "path": "packages/isar_flutter_libs/CHANGELOG.md",
    "content": "See [Isar Changelog](https://pub.dev/packages/isar/changelog)"
  },
  {
    "path": "packages/isar_flutter_libs/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "packages/isar_flutter_libs/README.md",
    "content": "### Flutter binaries for the [Isar Database](https://github.com/isar-community/isar) please go there for documentation."
  },
  {
    "path": "packages/isar_flutter_libs/android/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": "packages/isar_flutter_libs/android/build.gradle",
    "content": "group 'dev.isar.isar_flutter_libs'\nversion '1.0'\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.3.1'\n    }\n}\n\nrootProject.allprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\napply plugin: 'com.android.library'\n\nandroid {\n    if (project.android.hasProperty(\"namespace\")) {\n        namespace 'dev.isar.isar_flutter_libs'\n    }\n\n    compileSdkVersion 34\n\n    defaultConfig {\n        minSdkVersion 16\n    }\n}\n\ndependencies {\n    implementation \"androidx.startup:startup-runtime:1.1.1\"\n}"
  },
  {
    "path": "packages/isar_flutter_libs/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sat Nov 12 16:30:49 CET 2022\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4-bin.zip\ndistributionPath=wrapper/dists\nzipStorePath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\n"
  },
  {
    "path": "packages/isar_flutter_libs/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "packages/isar_flutter_libs/android/settings.gradle",
    "content": "rootProject.name = 'isar_flutter_libs'\n"
  },
  {
    "path": "packages/isar_flutter_libs/android/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"dev.isar.isar_flutter_libs\" />"
  },
  {
    "path": "packages/isar_flutter_libs/android/src/main/java/dev/isar/isar_flutter_libs/IsarFlutterLibsPlugin.java",
    "content": "package dev.isar.isar_flutter_libs;\n\nimport androidx.annotation.NonNull;\n\nimport io.flutter.embedding.engine.plugins.FlutterPlugin;\n\npublic class IsarFlutterLibsPlugin implements FlutterPlugin { \n    @Override\n    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { }\n\n    @Override\n    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { }\n}\n"
  },
  {
    "path": "packages/isar_flutter_libs/ios/.gitignore",
    "content": ".idea/\n.vagrant/\n.sconsign.dblite\n.svn/\n\n.DS_Store\n*.swp\nprofile\n\nDerivedData/\nbuild/\nGeneratedPluginRegistrant.h\nGeneratedPluginRegistrant.m\n\n.generated/\n\n*.pbxuser\n*.mode1v3\n*.mode2v3\n*.perspectivev3\n\n!default.pbxuser\n!default.mode1v3\n!default.mode2v3\n!default.perspectivev3\n\nxcuserdata\n\n*.moved-aside\n\n*.pyc\n*sync/\nIcon?\n.tags*\n\n/Flutter/Generated.xcconfig\n/Flutter/ephemeral/\n/Flutter/flutter_export_environment.sh"
  },
  {
    "path": "packages/isar_flutter_libs/ios/Assets/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/isar_flutter_libs/ios/Classes/IsarFlutterLibsPlugin.h",
    "content": "#import <Flutter/Flutter.h>\n\n@interface IsarFlutterLibsPlugin : NSObject<FlutterPlugin>\n@end\n"
  },
  {
    "path": "packages/isar_flutter_libs/ios/Classes/IsarFlutterLibsPlugin.m",
    "content": "#import \"IsarFlutterLibsPlugin.h\"\n#if __has_include(<isar_flutter_libs/isar_flutter_libs-Swift.h>)\n#import <isar_flutter_libs/isar_flutter_libs-Swift.h>\n#else\n// Support project import fallback if the generated compatibility header\n// is not copied when this plugin is created as a library.\n// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816\n#import \"isar_flutter_libs-Swift.h\"\n#endif\n\n@implementation IsarFlutterLibsPlugin\n+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {\n  [SwiftIsarFlutterLibsPlugin registerWithRegistrar:registrar];\n}\n@end\n"
  },
  {
    "path": "packages/isar_flutter_libs/ios/Classes/SwiftIsarFlutterLibsPlugin.swift",
    "content": "import Flutter\nimport UIKit\n\npublic class SwiftIsarFlutterLibsPlugin: NSObject, FlutterPlugin {\n  public static func register(with registrar: FlutterPluginRegistrar) {\n    }\n    \n    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {\n        result(nil)\n    }\n    \n    public func dummyMethodToEnforceBundling() {\n        // dummy calls to prevent tree shaking\n        isar_get_error(0)\n    }\n}\n"
  },
  {
    "path": "packages/isar_flutter_libs/ios/Classes/binding.h",
    "content": "#include <stdarg.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n\nchar* isar_get_error(uint32_t err);"
  },
  {
    "path": "packages/isar_flutter_libs/ios/Resources/PrivacyInfo.xcprivacy",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n  <dict>\n    <key>NSPrivacyTracking</key>\n    <false />\n    <key>NSPrivacyTrackingDomains</key>\n    <array />\n    <key>NSPrivacyCollectedDataTypes</key>\n    <array />\n    <key>NSPrivacyAccessedAPITypes</key>\n    <array>\n      <dict>\n        <key>NSPrivacyAccessedAPIType</key>\n        <string>NSPrivacyAccessedAPICategoryDiskSpace</string>\n        <key>NSPrivacyAccessedAPITypeReasons</key>\n        <array>\n          <string>E174.1</string>\n        </array>\n      </dict>\n    </array>\n  </dict>\n</plist>"
  },
  {
    "path": "packages/isar_flutter_libs/ios/isar_flutter_libs.podspec",
    "content": "Pod::Spec.new do |s|\n  s.name             = 'isar_flutter_libs'\n  s.version          = '1.0.0'\n  s.summary          = 'Flutter binaries for the Isar Database. Needs to be included for Flutter apps.'\n  s.homepage         = 'https://isar.dev'\n  s.license          = { :file => '../LICENSE' }\n  s.author           = { 'Isar' => 'hello@isar.dev' }\n\n  s.source           = { :path => '.' }\n  s.source_files     = 'Classes/**/*'\n  s.public_header_files = 'Classes/**/*.h'\n\n  s.dependency 'Flutter'\n  s.platform = :ios, '11.0'\n  s.swift_version = '5.3'\n  s.vendored_frameworks = 'isar.xcframework'\n  s.resource_bundles = {'isar_flutter_libs_apple_privacy' => ['Resources/PrivacyInfo.xcprivacy']}\nend\n"
  },
  {
    "path": "packages/isar_flutter_libs/lib/isar_flutter_libs.dart",
    "content": "library isar_flutter_libs;\n"
  },
  {
    "path": "packages/isar_flutter_libs/linux/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nset(PROJECT_NAME \"isar_flutter_libs\")\nproject(${PROJECT_NAME} LANGUAGES CXX)\n\n# This value is used when generating builds using this plugin, so it must\n# not be changed\nset(PLUGIN_NAME \"isar_flutter_libs_plugin\")\n\nadd_library(${PLUGIN_NAME} SHARED\n  \"isar_flutter_libs_plugin.cc\"\n)\napply_standard_settings(${PLUGIN_NAME})\nset_target_properties(${PLUGIN_NAME} PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)\ntarget_include_directories(${PLUGIN_NAME} INTERFACE\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE flutter)\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)\n\n# List of absolute paths to libraries that should be bundled with the plugin\nset(isar_flutter_libs_bundled_libraries\n  \"${CMAKE_CURRENT_SOURCE_DIR}/libisar.so\"\n  PARENT_SCOPE\n)\n"
  },
  {
    "path": "packages/isar_flutter_libs/linux/include/isar_flutter_libs/isar_flutter_libs_plugin.h",
    "content": "#ifndef FLUTTER_PLUGIN_ISAR_FLUTTER_LIBS_PLUGIN_H_\n#define FLUTTER_PLUGIN_ISAR_FLUTTER_LIBS_PLUGIN_H_\n\n#include <flutter_linux/flutter_linux.h>\n\nG_BEGIN_DECLS\n\n#ifdef FLUTTER_PLUGIN_IMPL\n#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define FLUTTER_PLUGIN_EXPORT\n#endif\n\ntypedef struct _IsarFlutterLibsPlugin IsarFlutterLibsPlugin;\ntypedef struct {\n  GObjectClass parent_class;\n} IsarFlutterLibsPluginClass;\n\nFLUTTER_PLUGIN_EXPORT GType isar_flutter_libs_plugin_get_type();\n\nFLUTTER_PLUGIN_EXPORT void isar_flutter_libs_plugin_register_with_registrar(\n    FlPluginRegistrar* registrar);\n\nG_END_DECLS\n\n#endif  // FLUTTER_PLUGIN_ISAR_FLUTTER_LIBS_PLUGIN_H_\n"
  },
  {
    "path": "packages/isar_flutter_libs/linux/isar_flutter_libs_plugin.cc",
    "content": "#include \"include/isar_flutter_libs/isar_flutter_libs_plugin.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#include <gtk/gtk.h>\n#include <sys/utsname.h>\n\n#include <cstring>\n\n#define ISAR_FLUTTER_LIBS_PLUGIN(obj) \\\n  (G_TYPE_CHECK_INSTANCE_CAST((obj), isar_flutter_libs_plugin_get_type(), \\\n                              IsarFlutterLibsPlugin))\n\nstruct _IsarFlutterLibsPlugin {\n  GObject parent_instance;\n};\n\nG_DEFINE_TYPE(IsarFlutterLibsPlugin, isar_flutter_libs_plugin, g_object_get_type())\n\n// Called when a method call is received from Flutter.\nstatic void isar_flutter_libs_plugin_handle_method_call(\n    IsarFlutterLibsPlugin* self,\n    FlMethodCall* method_call) {\n  g_autoptr(FlMethodResponse) response = nullptr;\n\n  const gchar* method = fl_method_call_get_name(method_call);\n\n  if (strcmp(method, \"getPlatformVersion\") == 0) {\n    struct utsname uname_data = {};\n    uname(&uname_data);\n    g_autofree gchar *version = g_strdup_printf(\"Linux %s\", uname_data.version);\n    g_autoptr(FlValue) result = fl_value_new_string(version);\n    response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));\n  } else {\n    response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());\n  }\n\n  fl_method_call_respond(method_call, response, nullptr);\n}\n\nstatic void isar_flutter_libs_plugin_dispose(GObject* object) {\n  G_OBJECT_CLASS(isar_flutter_libs_plugin_parent_class)->dispose(object);\n}\n\nstatic void isar_flutter_libs_plugin_class_init(IsarFlutterLibsPluginClass* klass) {\n  G_OBJECT_CLASS(klass)->dispose = isar_flutter_libs_plugin_dispose;\n}\n\nstatic void isar_flutter_libs_plugin_init(IsarFlutterLibsPlugin* self) {}\n\nstatic void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,\n                           gpointer user_data) {\n  IsarFlutterLibsPlugin* plugin = ISAR_FLUTTER_LIBS_PLUGIN(user_data);\n  isar_flutter_libs_plugin_handle_method_call(plugin, method_call);\n}\n\nvoid isar_flutter_libs_plugin_register_with_registrar(FlPluginRegistrar* registrar) {\n  IsarFlutterLibsPlugin* plugin = ISAR_FLUTTER_LIBS_PLUGIN(\n      g_object_new(isar_flutter_libs_plugin_get_type(), nullptr));\n\n  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();\n  g_autoptr(FlMethodChannel) channel =\n      fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),\n                            \"isar_flutter_libs\",\n                            FL_METHOD_CODEC(codec));\n  fl_method_channel_set_method_call_handler(channel, method_call_cb,\n                                            g_object_ref(plugin),\n                                            g_object_unref);\n\n  g_object_unref(plugin);\n}\n"
  },
  {
    "path": "packages/isar_flutter_libs/macos/Classes/IsarFlutterLibsPlugin.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\npublic class IsarFlutterLibsPlugin: NSObject, FlutterPlugin {\n  public static func register(with registrar: FlutterPluginRegistrar) {\n    let channel = FlutterMethodChannel(name: \"isar_flutter_libs\", binaryMessenger: registrar.messenger)\n    let instance = IsarFlutterLibsPlugin()\n    registrar.addMethodCallDelegate(instance, channel: channel)\n  }\n\n  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {\n    switch call.method {\n    case \"getPlatformVersion\":\n      result(\"macOS \" + ProcessInfo.processInfo.operatingSystemVersionString)\n    default:\n      result(FlutterMethodNotImplemented)\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_flutter_libs/macos/isar_flutter_libs.podspec",
    "content": "Pod::Spec.new do |s|\n  s.name             = 'isar_flutter_libs'\n  s.version          = '1.0.0'\n  s.summary          = 'Flutter binaries for the Isar Database. Needs to be included for Flutter apps.'\n  s.homepage         = 'https://isar.dev'\n  s.license          = { :file => '../LICENSE' }\n  s.author           = { 'Isar' => 'hello@isar.dev' }\n\n  s.source           = { :path => '.' }\n  s.source_files     = 'Classes/**/*'\n  s.public_header_files = 'Classes/**/*.h'\n\n  s.dependency 'FlutterMacOS'\n  s.platform = :osx, '10.11'\n  s.swift_version = '5.3'\n  s.vendored_libraries  = 'libisar.dylib'\nend"
  },
  {
    "path": "packages/isar_flutter_libs/pubspec.yaml",
    "content": "name: isar_flutter_libs\ndescription: Isar Core binaries for the Isar Database. Needs to be included for Flutter apps.\nversion: 3.1.8\nrepository: https://github.com/isar-community/isar\nhomepage: https://isar.dev\npublish_to: https://pub.isar-community.dev/\n\nenvironment:\n  sdk: \">=2.17.0 <3.0.0\"\n  flutter: \">=3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  isar: \n    version: 3.1.8\n    hosted: https://pub.isar-community.dev\n\nflutter:\n  plugin:\n    platforms:\n      android:\n        package: dev.isar.isar_flutter_libs\n        pluginClass: IsarFlutterLibsPlugin\n      ios:\n        pluginClass: IsarFlutterLibsPlugin\n      macos:\n        pluginClass: IsarFlutterLibsPlugin\n      linux:\n        pluginClass: IsarFlutterLibsPlugin\n      windows:\n        pluginClass: IsarFlutterLibsPlugin\n"
  },
  {
    "path": "packages/isar_flutter_libs/pubspec_overrides.yaml",
    "content": "dependency_overrides:\n isar: \n   path: ../isar"
  },
  {
    "path": "packages/isar_flutter_libs/windows/.gitignore",
    "content": "flutter/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "packages/isar_flutter_libs/windows/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nset(PROJECT_NAME \"isar_flutter_libs\")\nproject(${PROJECT_NAME} LANGUAGES CXX)\n\n# This value is used when generating builds using this plugin, so it must\n# not be changed\nset(PLUGIN_NAME \"isar_flutter_libs_plugin\")\n\nadd_library(${PLUGIN_NAME} SHARED\n  \"isar_flutter_libs_plugin.cpp\"\n)\napply_standard_settings(${PLUGIN_NAME})\nset_target_properties(${PLUGIN_NAME} PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)\ntarget_include_directories(${PLUGIN_NAME} INTERFACE\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)\n\n# List of absolute paths to libraries that should be bundled with the plugin\nset(isar_flutter_libs_bundled_libraries\n  \"${CMAKE_CURRENT_SOURCE_DIR}/isar.dll\"\n  PARENT_SCOPE\n)\n"
  },
  {
    "path": "packages/isar_flutter_libs/windows/include/isar_flutter_libs/isar_flutter_libs_plugin.h",
    "content": "#ifndef FLUTTER_PLUGIN_ISAR_FLUTTER_LIBS_PLUGIN_H_\n#define FLUTTER_PLUGIN_ISAR_FLUTTER_LIBS_PLUGIN_H_\n\n#include <flutter_plugin_registrar.h>\n\n#ifdef FLUTTER_PLUGIN_IMPL\n#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)\n#else\n#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)\n#endif\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\nFLUTTER_PLUGIN_EXPORT void IsarFlutterLibsPluginRegisterWithRegistrar(\n    FlutterDesktopPluginRegistrarRef registrar);\n\n#if defined(__cplusplus)\n}  // extern \"C\"\n#endif\n\n#endif  // FLUTTER_PLUGIN_ISAR_FLUTTER_LIBS_PLUGIN_H_\n"
  },
  {
    "path": "packages/isar_flutter_libs/windows/isar_flutter_libs_plugin.cpp",
    "content": "#include \"include/isar_flutter_libs/isar_flutter_libs_plugin.h\"\n\n// This must be included before many other Windows headers.\n#include <windows.h>\n\n// For getPlatformVersion; remove unless needed for your plugin implementation.\n#include <VersionHelpers.h>\n\n#include <flutter/method_channel.h>\n#include <flutter/plugin_registrar_windows.h>\n#include <flutter/standard_method_codec.h>\n\n#include <map>\n#include <memory>\n#include <sstream>\n\nnamespace {\n\nclass IsarFlutterLibsPlugin : public flutter::Plugin {\n public:\n  static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);\n\n  IsarFlutterLibsPlugin();\n\n  virtual ~IsarFlutterLibsPlugin();\n\n private:\n  // Called when a method is called on this plugin's channel from Dart.\n  void HandleMethodCall(\n      const flutter::MethodCall<flutter::EncodableValue> &method_call,\n      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);\n};\n\n// static\nvoid IsarFlutterLibsPlugin::RegisterWithRegistrar(\n    flutter::PluginRegistrarWindows *registrar) {\n  auto channel =\n      std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(\n          registrar->messenger(), \"isar_flutter_libs\",\n          &flutter::StandardMethodCodec::GetInstance());\n\n  auto plugin = std::make_unique<IsarFlutterLibsPlugin>();\n\n  channel->SetMethodCallHandler(\n      [plugin_pointer = plugin.get()](const auto &call, auto result) {\n        plugin_pointer->HandleMethodCall(call, std::move(result));\n      });\n\n  registrar->AddPlugin(std::move(plugin));\n}\n\nIsarFlutterLibsPlugin::IsarFlutterLibsPlugin() {}\n\nIsarFlutterLibsPlugin::~IsarFlutterLibsPlugin() {}\n\nvoid IsarFlutterLibsPlugin::HandleMethodCall(\n    const flutter::MethodCall<flutter::EncodableValue> &method_call,\n    std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {\n  if (method_call.method_name().compare(\"getPlatformVersion\") == 0) {\n    std::ostringstream version_stream;\n    version_stream << \"Windows \";\n    if (IsWindows10OrGreater()) {\n      version_stream << \"10+\";\n    } else if (IsWindows8OrGreater()) {\n      version_stream << \"8\";\n    } else if (IsWindows7OrGreater()) {\n      version_stream << \"7\";\n    }\n    result->Success(flutter::EncodableValue(version_stream.str()));\n  } else {\n    result->NotImplemented();\n  }\n}\n\n}  // namespace\n\nvoid IsarFlutterLibsPluginRegisterWithRegistrar(\n    FlutterDesktopPluginRegistrarRef registrar) {\n  IsarFlutterLibsPlugin::RegisterWithRegistrar(\n      flutter::PluginRegistrarManager::GetInstance()\n          ->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));\n}\n"
  },
  {
    "path": "packages/isar_generator/CHANGELOG.md",
    "content": "See [Isar Changelog](https://pub.dev/packages/isar/changelog)"
  },
  {
    "path": "packages/isar_generator/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "packages/isar_generator/README.md",
    "content": "### Code generator for the [Isar Database](https://github.com/isar-community/isar) please go there for documentation."
  },
  {
    "path": "packages/isar_generator/analysis_options.yaml",
    "content": "include: package:very_good_analysis/analysis_options.yaml\n\nanalyzer:\n  errors:\n    cascade_invocations: ignore\n    avoid_positional_boolean_parameters: ignore\n    parameter_assignments: ignore\n    public_member_api_docs: ignore\n    use_string_buffers: ignore"
  },
  {
    "path": "packages/isar_generator/build.yaml",
    "content": "builders:\n  isar_generator:\n    import: \"package:isar_generator/isar_generator.dart\"\n    builder_factories: [\"getIsarGenerator\"]\n    build_extensions: { \".dart\": [\"isar_generator.g.part\"] }\n    auto_apply: dependents\n    build_to: cache\n    applies_builders: [\"source_gen|combining_builder\"]\n"
  },
  {
    "path": "packages/isar_generator/lib/isar_generator.dart",
    "content": "import 'package:build/build.dart';\nimport 'package:isar_generator/src/collection_generator.dart';\nimport 'package:source_gen/source_gen.dart';\n\nBuilder getIsarGenerator(BuilderOptions options) => SharedPartBuilder(\n      [\n        IsarCollectionGenerator(),\n        IsarEmbeddedGenerator(),\n      ],\n      'isar_generator',\n    );\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/by_index_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nString generateByIndexExtension(ObjectInfo oi) {\n  final uniqueIndexes = oi.indexes.where((e) => e.unique).toList();\n  if (uniqueIndexes.isEmpty) {\n    return '';\n  }\n  var code =\n      'extension ${oi.dartName}ByIndex on IsarCollection<${oi.dartName}> {';\n  for (final index in uniqueIndexes) {\n    code += generateSingleByIndex(oi, index);\n    code += generateAllByIndex(oi, index);\n    if (!index.properties.first.isMultiEntry) {\n      code += generatePutByIndex(oi, index);\n    }\n  }\n  return '''\n    $code\n  }''';\n}\n\nextension on ObjectIndex {\n  String get dartName {\n    return properties.map((e) => e.property.dartName.capitalize()).join();\n  }\n}\n\nString generateSingleByIndex(ObjectInfo oi, ObjectIndex index) {\n  final params = index.properties\n      .map((i) => '${i.property.dartType} ${i.property.dartName}')\n      .join(',');\n  final paramsList = index.properties.map((i) => i.property.dartName).join(',');\n  return '''\n    Future<${oi.dartName}?> getBy${index.dartName}($params) {\n      return getByIndex(r'${index.name}', [$paramsList]);\n    }\n\n    ${oi.dartName}? getBy${index.dartName}Sync($params) {\n      return getByIndexSync(r'${index.name}', [$paramsList]);\n    }\n\n    Future<bool> deleteBy${index.dartName}($params) {\n      return deleteByIndex(r'${index.name}', [$paramsList]);\n    }\n\n    bool deleteBy${index.dartName}Sync($params) {\n      return deleteByIndexSync(r'${index.name}', [$paramsList]);\n    }\n  ''';\n}\n\nString generateAllByIndex(ObjectInfo oi, ObjectIndex index) {\n  String valsName(ObjectProperty p) => '${p.dartName}Values';\n\n  final props = index.properties;\n  final params = props\n      .map((ip) => 'List<${ip.property.dartType}> ${valsName(ip.property)}')\n      .join(',');\n  String createValues;\n  if (props.length == 1) {\n    final p = props.first.property;\n    createValues = 'final values = ${valsName(p)}.map((e) => [e]).toList();';\n  } else {\n    final lenAssert = props\n        .sublist(1)\n        .map((i) => '${valsName(i.property)}.length == len')\n        .join('&&');\n    createValues = '''\n      final len = ${valsName(props.first.property)}.length;\n      assert($lenAssert, 'All index values must have the same length');\n      final values = <List<dynamic>>[];\n      for (var i = 0; i < len; i++) {\n        values.add([${props.map((ip) => '${valsName(ip.property)}[i]').join(',')}]);\n      }\n    ''';\n  }\n  return '''\n    Future<List<${oi.dartName}?>> getAllBy${index.dartName}($params) {\n      $createValues\n      return getAllByIndex(r'${index.name}', values);\n    }\n\n    List<${oi.dartName}?> getAllBy${index.dartName}Sync($params) {\n      $createValues\n      return getAllByIndexSync(r'${index.name}', values);\n    }\n\n    Future<int> deleteAllBy${index.dartName}($params) {\n      $createValues\n      return deleteAllByIndex(r'${index.name}', values);\n    }\n\n    int deleteAllBy${index.dartName}Sync($params) {\n      $createValues\n      return deleteAllByIndexSync(r'${index.name}', values);\n    }\n  ''';\n}\n\nString generatePutByIndex(ObjectInfo oi, ObjectIndex index) {\n  return '''\n    Future<Id> putBy${index.dartName}(${oi.dartName} object) {\n      return putByIndex(r'${index.name}', object);\n    }\n\n    Id putBy${index.dartName}Sync(${oi.dartName} object, {bool saveLinks = true}) {\n      return putByIndexSync(r'${index.name}', object, saveLinks: saveLinks);\n    }\n\n    Future<List<Id>> putAllBy${index.dartName}(List<${oi.dartName}> objects) {\n      return putAllByIndex(r'${index.name}', objects);\n    }\n\n    List<Id> putAllBy${index.dartName}Sync(List<${oi.dartName}> objects, {bool saveLinks = true}) {\n      return putAllByIndexSync(r'${index.name}', objects, saveLinks: saveLinks);\n    }\n  ''';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/collection_schema_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/isar_type.dart';\n\nimport 'package:isar_generator/src/object_info.dart';\n\nString generateSchema(ObjectInfo object) {\n  var code = 'const ${object.dartName.capitalize()}Schema = ';\n  if (!object.isEmbedded) {\n    code += 'CollectionSchema(';\n  } else {\n    code += 'Schema(';\n  }\n\n  final properties = object.objectProperties\n      .mapIndexed(\n        (i, e) => \"r'${e.isarName}': ${_generatePropertySchema(object, i)}\",\n      )\n      .join(',');\n\n  code += '''\n    name: r'${object.isarName}',\n    id: ${object.id},\n    properties: {$properties},\n\n    estimateSize: ${object.estimateSizeName},\n    serialize: ${object.serializeName},\n    deserialize: ${object.deserializeName},\n    deserializeProp: ${object.deserializePropName},''';\n\n  if (!object.isEmbedded) {\n    final indexes = object.indexes\n        .map((e) => \"r'${e.name}': ${_generateIndexSchema(e)}\")\n        .join(',');\n    final links = object.links\n        .map((e) => \"r'${e.isarName}': ${_generateLinkSchema(object, e)}\")\n        .join(',');\n    final embeddedSchemas = object.embeddedDartNames.entries\n        .map((e) => \"r'${e.key}': ${e.value.capitalize()}Schema\")\n        .join(',');\n\n    code += '''\n      idName: r'${object.idProperty.isarName}',\n      indexes: {$indexes},\n      links: {$links},\n      embeddedSchemas: {$embeddedSchemas},\n\n      getId: ${object.getIdName},\n      getLinks: ${object.getLinksName},\n      attach: ${object.attachName},\n      version: '${Isar.version}',\n    ''';\n  }\n\n  return '$code);';\n}\n\nString _generatePropertySchema(ObjectInfo object, int index) {\n  final property = object.objectProperties[index];\n  var enumMap = '';\n  if (property.isEnum) {\n    enumMap = 'enumMap: ${property.enumValueMapName(object)},';\n  }\n  var target = '';\n  if (property.targetIsarName != null) {\n    target = \"target: r'${property.targetIsarName}',\";\n  }\n  return '''\n  PropertySchema(\n    id: $index,\n    name: r'${property.isarName}',\n    type: IsarType.${property.isarType.name},\n    $enumMap\n    $target\n  )\n  ''';\n}\n\nString _generateIndexSchema(ObjectIndex index) {\n  final properties = index.properties.map((e) {\n    return '''\n      IndexPropertySchema(\n        name: r'${e.property.isarName}',\n        type: IndexType.${e.type.name},\n        caseSensitive: ${e.caseSensitive},\n      )''';\n  }).join(',');\n\n  return '''\n    IndexSchema(\n      id: ${index.id},\n      name: r'${index.name}',\n      unique: ${index.unique},\n      replace: ${index.replace},\n      properties: [$properties],\n    )''';\n}\n\nString _generateLinkSchema(ObjectInfo object, ObjectLink link) {\n  var linkName = '';\n  if (link.isBacklink) {\n    linkName = \"linkName: r'${link.targetLinkIsarName}',\";\n  }\n  return '''\n    LinkSchema(\n      id: ${link.id(object.isarName)},\n      name: r'${link.isarName}',\n      target: r'${link.targetCollectionIsarName}',\n      single: ${link.isSingle},\n      $linkName\n    )''';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_distinct_by_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/isar_type.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nString generateDistinctBy(ObjectInfo oi) {\n  var code = '''\n  extension ${oi.dartName}QueryWhereDistinct on QueryBuilder<${oi.dartName}, ${oi.dartName}, QDistinct> {''';\n  for (final property in oi.objectProperties) {\n    if (property.isarType == IsarType.string) {\n      code += '''\n        QueryBuilder<${oi.dartName}, ${oi.dartName}, QDistinct>distinctBy${property.dartName.capitalize()}({bool caseSensitive = true}) {\n          return QueryBuilder.apply(this, (query) {\n            return query.addDistinctBy(r'${property.isarName}', caseSensitive: caseSensitive);\n          });\n        }''';\n    } else if (!property.isarType.containsObject) {\n      code += '''\n        QueryBuilder<${oi.dartName}, ${oi.dartName}, QDistinct>distinctBy${property.dartName.capitalize()}() {\n          return QueryBuilder.apply(this, (query) {\n            return query.addDistinctBy(r'${property.isarName}');\n          });\n        }''';\n    }\n  }\n  return '$code}';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_filter_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/code_gen/query_filter_length.dart';\nimport 'package:isar_generator/src/isar_type.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nclass FilterGenerator {\n  FilterGenerator(this.object) : objName = object.dartName;\n  final ObjectInfo object;\n  final String objName;\n\n  String generate() {\n    var code =\n        'extension ${objName}QueryFilter on QueryBuilder<$objName, $objName, '\n        'QFilterCondition> {';\n    for (final property in object.properties) {\n      if (property.nullable) {\n        code += generateIsNull(property);\n        code += generateIsNotNull(property);\n      }\n      if (property.elementNullable) {\n        code += generateElementIsNull(property);\n        code += generateElementIsNotNull(property);\n      }\n\n      if (!property.isarType.containsObject) {\n        code += generateEqualTo(property);\n\n        if (!property.isarType.containsBool) {\n          code += generateGreaterThan(property);\n          code += generateLessThan(property);\n          code += generateBetween(property);\n        }\n      }\n\n      if (property.isarType.containsString) {\n        code += generateStringStartsWith(property);\n        code += generateStringEndsWith(property);\n        code += generateStringContains(property);\n        code += generateStringMatches(property);\n        code += generateStringIsEmpty(property);\n        code += generateStringIsNotEmpty(property);\n      }\n\n      if (property.isarType.isList) {\n        code += generateListLength(property);\n      }\n    }\n    return '''\n    $code\n  }''';\n  }\n\n  String mPrefix(ObjectProperty p, [bool listElement = true]) {\n    final any = listElement && p.isarType.isList ? 'Element' : '';\n    return 'QueryBuilder<$objName, $objName, QAfterFilterCondition> '\n        '${p.dartName.decapitalize()}$any';\n  }\n\n  String generateEqualTo(ObjectProperty p) {\n    final optional = [\n      if (p.isarType.containsString) 'bool caseSensitive = true',\n      if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    ${mPrefix(p)}EqualTo(${p.nScalarDartType} value ${optional.isNotBlank ? ', {$optional,}' : ''}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.equalTo(\n          property: r'${p.isarName}',\n          value: value,\n          ${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}\n          ${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }''';\n  }\n\n  String generateGreaterThan(ObjectProperty p) {\n    final optional = [\n      'bool include = false',\n      if (p.isarType.containsString) 'bool caseSensitive = true',\n      if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    ${mPrefix(p)}GreaterThan(${p.nScalarDartType} value, {$optional,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.greaterThan(\n          include: include,\n          property: r'${p.isarName}',\n          value: value,\n          ${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}\n          ${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }''';\n  }\n\n  String generateLessThan(ObjectProperty p) {\n    final optional = [\n      'bool include = false',\n      if (p.isarType.containsString) 'bool caseSensitive = true',\n      if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    ${mPrefix(p)}LessThan(${p.nScalarDartType} value, {$optional,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.lessThan(\n          include: include,\n          property: r'${p.isarName}',\n          value: value,\n          ${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}\n          ${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }''';\n  }\n\n  String generateBetween(ObjectProperty p) {\n    final optional = [\n      'bool includeLower = true',\n      'bool includeUpper = true',\n      if (p.isarType.containsString) 'bool caseSensitive = true',\n      if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    ${mPrefix(p)}Between(${p.nScalarDartType} lower, ${p.nScalarDartType} upper, {$optional,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.between(\n          property: r'${p.isarName}',\n          lower: lower,\n          includeLower: includeLower,\n          upper: upper,\n          includeUpper: includeUpper,\n          ${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}\n          ${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }''';\n  }\n\n  String generateIsNull(ObjectProperty p) {\n    return '''\n      ${mPrefix(p, false)}IsNull() {\n        return QueryBuilder.apply(this, (query) {\n          return query.addFilterCondition(const FilterCondition.isNull(\n            property: r'${p.isarName}',\n          ));\n        });\n      }''';\n  }\n\n  String generateElementIsNull(ObjectProperty p) {\n    return '''\n      ${mPrefix(p)}IsNull() {\n        return QueryBuilder.apply(this, (query) {\n          return query.addFilterCondition(const FilterCondition.elementIsNull(\n            property: r'${p.isarName}',\n          ));\n        });\n      }''';\n  }\n\n  String generateIsNotNull(ObjectProperty p) {\n    return '''\n      ${mPrefix(p, false)}IsNotNull() {\n        return QueryBuilder.apply(this, (query) {\n          return query\n            .addFilterCondition(const FilterCondition.isNotNull(\n              property: r'${p.isarName}',\n            ));\n        });\n      }''';\n  }\n\n  String generateElementIsNotNull(ObjectProperty p) {\n    return '''\n      ${mPrefix(p)}IsNotNull() {\n        return QueryBuilder.apply(this, (query) {\n          return query\n            .addFilterCondition(const FilterCondition.elementIsNotNull(\n              property: r'${p.isarName}',\n            ));\n        });\n      }''';\n  }\n\n  String generateStringStartsWith(ObjectProperty p) {\n    return '''\n    ${mPrefix(p)}StartsWith(String value, {bool caseSensitive = true,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.startsWith(\n          property: r'${p.isarName}',\n          value: value,\n          caseSensitive: caseSensitive,\n        ));\n      });\n    }''';\n  }\n\n  String generateStringEndsWith(ObjectProperty p) {\n    return '''\n    ${mPrefix(p)}EndsWith(String value, {bool caseSensitive = true,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.endsWith(\n          property: r'${p.isarName}',\n          value: value,\n          caseSensitive: caseSensitive,\n        ));\n      });\n    }''';\n  }\n\n  String generateStringContains(ObjectProperty p) {\n    return '''\n    ${mPrefix(p)}Contains(String value, {bool caseSensitive = true}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.contains(\n          property: r'${p.isarName}',\n          value: value,\n          caseSensitive: caseSensitive,\n        ));\n      });\n    }''';\n  }\n\n  String generateStringMatches(ObjectProperty p) {\n    return '''\n    ${mPrefix(p)}Matches(String pattern, {bool caseSensitive = true}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.matches(\n          property: r'${p.isarName}',\n          wildcard: pattern,\n          caseSensitive: caseSensitive,\n        ));\n      });\n    }''';\n  }\n\n  String generateStringIsEmpty(ObjectProperty p) {\n    return '''\n    ${mPrefix(p)}IsEmpty() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.equalTo(\n          property: r'${p.isarName}',\n          value: '',\n        ));\n      });\n    }''';\n  }\n\n  String generateStringIsNotEmpty(ObjectProperty p) {\n    return '''\n    ${mPrefix(p)}IsNotEmpty() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addFilterCondition(FilterCondition.greaterThan(\n          property: r'${p.isarName}',\n          value: '',\n        ));\n      });\n    }''';\n  }\n\n  String generateListLength(ObjectProperty p) {\n    return generateLength(objName, p.dartName,\n        (lower, includeLower, upper, includeUpper) {\n      return '''\n        QueryBuilder.apply(this, (query) {\n          return query.listLength(\n            r'${p.isarName}',\n            $lower,\n            $includeLower,\n            $upper,\n            $includeUpper,\n          );\n        })''';\n    });\n  }\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_filter_length.dart",
    "content": "import 'package:dartx/dartx.dart';\n\nString generateLength(\n  String objectName,\n  String propertyName,\n  String Function(\n    String lower,\n    String includeLower,\n    String upper,\n    String includeUpper,\n  ) codeGen,\n) {\n  return '''\n      QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthEqualTo(int length) {\n        return ${codeGen('length', 'true', 'length', 'true')};\n      }\n\n      QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}IsEmpty() {\n        return ${codeGen('0', 'true', '0', 'true')};\n      }\n\n      QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}IsNotEmpty() {\n        return ${codeGen('0', 'false', '999999', 'true')};\n      }\n\n      QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthLessThan(\n        int length, {\n        bool include = false,\n      }) {\n        return ${codeGen('0', 'true', 'length', 'include')};\n      }\n\n      QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthGreaterThan(\n        int length, {\n        bool include = false,\n      }) {\n        return ${codeGen('length', 'include', '999999', 'true')};\n      }\n\n      QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthBetween(\n        int lower, \n        int upper, {\n        bool includeLower = true,\n        bool includeUpper = true,\n      }) {\n        return ${codeGen('lower', 'includeLower', 'upper', 'includeUpper')};\n      }\n      ''';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_link_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar_generator/src/code_gen/query_filter_length.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nString generateQueryLinks(ObjectInfo oi) {\n  var code =\n      'extension ${oi.dartName}QueryLinks on QueryBuilder<${oi.dartName}, '\n      '${oi.dartName}, QFilterCondition> {';\n  for (final link in oi.links) {\n    code += '''\n      QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterFilterCondition> ${link.dartName.decapitalize()}(FilterQuery<${link.targetCollectionDartName}> q) {\n        return QueryBuilder.apply(this, (query) {\n          return query.link(q, r'${link.isarName}');\n        });\n      }''';\n\n    if (link.isSingle) {\n      code += '''\n        QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterFilterCondition> ${link.dartName.decapitalize()}IsNull() {\n          return QueryBuilder.apply(this, (query) {\n            return query.linkLength(r'${link.isarName}', 0, true, 0, true);\n          });\n        }''';\n    } else {\n      code += generateLength(oi.dartName, link.dartName,\n          (lower, includeLower, upper, includeUpper) {\n        return '''\n        QueryBuilder.apply(this, (query) {\n          return query.linkLength(r'${link.isarName}', $lower, $includeLower, $upper, $includeUpper);\n        })''';\n      });\n    }\n  }\n\n  return '''\n    $code\n  }''';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_object_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/isar_type.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nString generateQueryObjects(ObjectInfo oi) {\n  var code =\n      'extension ${oi.dartName}QueryObject on QueryBuilder<${oi.dartName}, '\n      '${oi.dartName}, QFilterCondition> {';\n  for (final property in oi.objectProperties) {\n    if (!property.isarType.containsObject) {\n      continue;\n    }\n    var name = property.dartName.decapitalize();\n    if (property.isarType.isList) {\n      name += 'Element';\n    }\n    code += '''\n      QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterFilterCondition> $name(FilterQuery<${property.typeClassName}> q) {\n        return QueryBuilder.apply(this, (query) {\n          return query.object(q, r'${property.isarName}');\n        });\n      }''';\n  }\n\n  return '''\n    $code\n  }''';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_property_generator.dart",
    "content": "import 'package:isar_generator/src/object_info.dart';\n\nString generatePropertyQuery(ObjectInfo oi) {\n  var code = '''\n  extension ${oi.dartName}QueryProperty on QueryBuilder<${oi.dartName}, ${oi.dartName}, QQueryProperty> {''';\n\n  // Ids are always non-nullable regardless of their specified nullability\n  code += '''\n      QueryBuilder<${oi.dartName}, int, QQueryOperations>${oi.idProperty.dartName}Property() {\n        return QueryBuilder.apply(this, (query) {\n          return query.addPropertyName(r'${oi.idProperty.isarName}');\n        });\n      }''';\n\n  for (final property in oi.objectProperties) {\n    code += '''\n      QueryBuilder<${oi.dartName}, ${property.dartType}, QQueryOperations>${property.dartName}Property() {\n        return QueryBuilder.apply(this, (query) {\n          return query.addPropertyName(r'${property.isarName}');\n        });\n      }''';\n  }\n\n  return '$code}';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_sort_by_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/isar_type.dart';\n\nimport 'package:isar_generator/src/object_info.dart';\n\nString generateSortBy(ObjectInfo oi) {\n  var code = '''\n  extension ${oi.dartName}QuerySortBy on QueryBuilder<${oi.dartName}, ${oi.dartName}, QSortBy> {''';\n\n  for (final property in oi.objectProperties) {\n    if (property.isarType.isList || property.isarType.containsObject) {\n      continue;\n    }\n\n    code += '''\n    QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>sortBy${property.dartName.capitalize()}() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addSortBy(r'${property.isarName}', Sort.asc);\n      });\n    }\n    \n    QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>sortBy${property.dartName.capitalize()}Desc() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addSortBy(r'${property.isarName}', Sort.desc);\n      });\n    }''';\n  }\n\n  code += '''\n  }\n\n  extension ${oi.dartName}QuerySortThenBy on QueryBuilder<${oi.dartName}, ${oi.dartName}, QSortThenBy> {''';\n\n  for (final property in oi.properties) {\n    if (property.isarType.isList || property.isarType.containsObject) {\n      continue;\n    }\n\n    code += '''\n    QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>thenBy${property.dartName.capitalize()}() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addSortBy(r'${property.isarName}', Sort.asc);\n      });\n    }\n    \n    QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>thenBy${property.dartName.capitalize()}Desc() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addSortBy(r'${property.isarName}', Sort.desc);\n      });\n    }''';\n  }\n\n  return '$code}';\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/query_where_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nclass WhereGenerator {\n  WhereGenerator(this.object)\n      : objName = object.dartName,\n        id = object.idProperty;\n  final ObjectInfo object;\n  final String objName;\n  final ObjectProperty id;\n  final existing = <String>{};\n\n  String generate() {\n    var code = 'extension ${objName}QueryWhereSort on QueryBuilder<$objName, '\n        '$objName, QWhere> {';\n\n    code += generateAnyId();\n    for (final index in object.indexes) {\n      if (index.properties.all((element) => element.type == IndexType.value)) {\n        code += generateAny(index);\n      }\n    }\n\n    code += '''\n  }\n\n  extension ${objName}QueryWhere on QueryBuilder<$objName, $objName, QWhereClause> {\n  ''';\n\n    code += generateWhereIdEqualTo();\n    code += generateWhereIdNotEqualTo();\n    code += generateWhereIdGreaterThan();\n    code += generateWhereIdLessThan();\n    code += generateWhereIdBetween();\n\n    for (final index in object.indexes) {\n      for (var n = 0; n < index.properties.length; n++) {\n        final indexProperty = index.properties[n];\n        final property = indexProperty.property;\n\n        if ((property.nullable && !indexProperty.isMultiEntry) ||\n            (property.elementNullable && indexProperty.isMultiEntry)) {\n          code += generateWhereIsNull(index, n + 1);\n          code += generateWhereIsNotNull(index, n + 1);\n        }\n\n        code += generateWhereEqualTo(index, n + 1);\n        code += generateWhereNotEqualTo(index, n + 1);\n\n        if (indexProperty.type == IndexType.value) {\n          if (property.isarType != IsarType.bool &&\n              property.isarType != IsarType.boolList) {\n            code += generateWhereGreaterThan(index, n + 1);\n            code += generateWhereLessThan(index, n + 1);\n            code += generateWhereBetween(index, n + 1);\n          }\n\n          if (property.isarType == IsarType.string ||\n              property.isarType == IsarType.stringList) {\n            code += generateWhereStartsWith(index, n + 1);\n            code += generateStringIsEmpty(index, n + 1);\n            code += generateStringIsNotEmpty(index, n + 1);\n          }\n        }\n      }\n    }\n\n    return '$code}';\n  }\n\n  String getMethodName(ObjectIndex index, int propertyCount, [String? method]) {\n    String propertyName(ObjectIndexProperty p) {\n      var name = p.property.dartName.capitalize();\n      if (p.isMultiEntry) {\n        name += 'Element';\n      }\n      return name;\n    }\n\n    var name = '';\n    final eqProperties =\n        index.properties.sublist(0, propertyCount - (method != null ? 1 : 0));\n    if (eqProperties.isNotEmpty) {\n      name += eqProperties.map(propertyName).join();\n      name += 'EqualTo';\n    }\n\n    if (method != null) {\n      name += propertyName(index.properties[propertyCount - 1]);\n      name += method;\n    }\n\n    final remainingProperties = propertyCount < index.properties.length\n        ? index.properties.sublist(propertyCount)\n        : null;\n\n    if (remainingProperties != null) {\n      name += 'Any';\n      name += remainingProperties.map(propertyName).join();\n    }\n\n    return name.decapitalize();\n  }\n\n  String paramType(ObjectIndexProperty p) {\n    if (p.property.isarType.isList && p.type == IndexType.hash) {\n      return p.property.dartType;\n    } else {\n      return p.property.nScalarDartType;\n    }\n  }\n\n  String paramName(ObjectIndexProperty p) {\n    if (p.property.isarType.isList && p.type != IndexType.hash) {\n      return '${p.property.dartName}Element';\n    } else {\n      return p.property.dartName;\n    }\n  }\n\n  String joinToParams(List<ObjectIndexProperty> properties) {\n    return properties\n        .map((it) => '${paramType(it)} ${paramName(it)}')\n        .join(',');\n  }\n\n  String joinToValues(List<ObjectIndexProperty> properties) {\n    return properties.map((it) {\n      if (it.property.isarType.isList && it.type != IndexType.hash) {\n        return '${it.property.dartName}Element';\n      } else {\n        return paramName(it);\n      }\n    }).join(', ');\n  }\n\n  String generateAnyId() {\n    return '''\n    QueryBuilder<$objName, $objName, QAfterWhere> any${id.dartName.capitalize()}() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(const IdWhereClause.any());\n      });\n    }\n    ''';\n  }\n\n  String generateAny(ObjectIndex index) {\n    final name = getMethodName(index, 0);\n    if (!existing.add(name)) {\n      return '';\n    }\n    return '''\n    QueryBuilder<$objName, $objName, QAfterWhere> $name() {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(\n          const IndexWhereClause.any(indexName: r'${index.name}'),\n        );\n      });\n    }\n    ''';\n  }\n\n  String get mPrefix => 'QueryBuilder<$objName, $objName, QAfterWhereClause>';\n\n  String generateWhereIdEqualTo() {\n    final idName = id.dartName.decapitalize();\n    return '''\n    $mPrefix ${idName}EqualTo(Id $idName) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IdWhereClause.between(\n          lower: $idName,\n          upper: $idName,\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateWhereEqualTo(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount);\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount);\n    final values = joinToValues(properties);\n    final params = joinToParams(properties);\n    return '''\n    $mPrefix $name($params ${properties.containsFloat ? ', {double epsilon = Query.epsilon,}' : ''}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.equalTo(\n          indexName: r'${index.name}',\n          value: [$values],\n          ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateWhereIdNotEqualTo() {\n    final idName = id.dartName.decapitalize();\n    return '''\n    $mPrefix ${idName}NotEqualTo(Id $idName) {\n      return QueryBuilder.apply(this, (query) {\n        if (query.whereSort == Sort.asc) {\n          return query.addWhereClause(\n            IdWhereClause.lessThan(upper: $idName, includeUpper: false),\n          ).addWhereClause(\n            IdWhereClause.greaterThan(lower: $idName, includeLower: false),\n          );\n        } else {\n          return query.addWhereClause(\n            IdWhereClause.greaterThan(lower: $idName, includeLower: false),\n          ).addWhereClause(\n            IdWhereClause.lessThan(upper: $idName, includeUpper: false),\n          );\n        }\n      });\n    }\n    ''';\n  }\n\n  String generateWhereNotEqualTo(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'NotEqualTo');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount);\n    final params = joinToParams(properties);\n\n    final equalProperties = properties.dropLast(1);\n    final notEqualProperty = properties.last;\n    final equalValues = joinToValues(equalProperties);\n    var notEqualValue = joinToValues([notEqualProperty]);\n    if (equalValues.isNotEmpty) {\n      notEqualValue = ',$notEqualValue';\n    }\n\n    return '''\n    $mPrefix $name($params ${properties.containsFloat ? ', {double epsilon = Query.epsilon,}' : ''}) {\n      return QueryBuilder.apply(this, (query) {\n        if (query.whereSort == Sort.asc) {\n          return query.addWhereClause(IndexWhereClause.between(\n            indexName: r'${index.name}',\n            lower: [$equalValues],\n            upper: [$equalValues $notEqualValue],\n            includeUpper: false,\n            ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n          )).addWhereClause(IndexWhereClause.between(\n            indexName: r'${index.name}',\n            lower: [$equalValues $notEqualValue],\n            includeLower: false,\n            upper: [$equalValues],\n            ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n          ));\n        } else {\n          return query.addWhereClause(IndexWhereClause.between(\n            indexName: r'${index.name}',\n            lower: [$equalValues $notEqualValue],\n            includeLower: false,\n            upper: [$equalValues],\n            ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n          )).addWhereClause(IndexWhereClause.between(\n            indexName: r'${index.name}',\n            lower: [$equalValues],\n            upper: [$equalValues $notEqualValue],\n            includeUpper: false,\n            ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n          ));\n        }\n      });\n    }\n    ''';\n  }\n\n  String generateWhereIdGreaterThan() {\n    final idName = id.dartName.decapitalize();\n    return '''\n    $mPrefix ${idName}GreaterThan(Id $idName, {bool include = false}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(\n          IdWhereClause.greaterThan(lower: $idName, includeLower: include),\n        );\n      });\n    }\n    ''';\n  }\n\n  String generateWhereGreaterThan(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'GreaterThan');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount);\n    final optional = [\n      'bool include = false',\n      if (properties.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    $mPrefix $name(${joinToParams(properties)}, {$optional,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.between(\n          indexName: r'${index.name}',\n          lower: [${joinToValues(properties)}],\n          includeLower: include,\n          upper: [${joinToValues(properties.dropLast(1))}],\n          ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateWhereIdLessThan() {\n    final idName = id.dartName.decapitalize();\n    return '''\n    $mPrefix ${idName}LessThan(Id $idName, {bool include = false}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(\n          IdWhereClause.lessThan(upper: $idName, includeUpper: include),\n        );\n      });\n    }\n    ''';\n  }\n\n  String generateWhereLessThan(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'LessThan');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount);\n    final optional = [\n      'bool include = false',\n      if (properties.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    $mPrefix $name(${joinToParams(properties)}, {$optional,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.between(\n          indexName: r'${index.name}',\n          lower: [${joinToValues(properties.dropLast(1))}],\n          upper: [${joinToValues(properties)}],\n          includeUpper: include,\n          ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateWhereIdBetween() {\n    final idName = id.dartName.decapitalize();\n    final lowerName = 'lower${id.dartName.capitalize()}';\n    final upperName = 'upper${id.dartName.capitalize()}';\n    return '''\n    $mPrefix ${idName}Between(Id $lowerName, Id $upperName, {bool includeLower = true, bool includeUpper = true,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IdWhereClause.between(\n          lower: $lowerName,\n          includeLower: includeLower,\n          upper: $upperName,\n          includeUpper: includeUpper,\n        ));\n      });\n    }\n  ''';\n  }\n\n  String generateWhereBetween(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'Between');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount);\n    final equalProperties = properties.dropLast(1);\n    final betweenProperty = properties.last;\n    var params = joinToParams(equalProperties);\n    if (params.isNotEmpty) {\n      params += ',';\n    }\n\n    final betweenType = paramType(betweenProperty);\n    final lowerName = 'lower${paramName(betweenProperty).capitalize()}';\n    final upperName = 'upper${paramName(betweenProperty).capitalize()}';\n    params += '$betweenType $lowerName, $betweenType $upperName';\n\n    var values = joinToValues(equalProperties);\n    if (values.isNotEmpty) {\n      values += ',';\n    }\n\n    final optional = [\n      'bool includeLower = true',\n      'bool includeUpper = true',\n      if (properties.containsFloat) 'double epsilon = Query.epsilon',\n    ].join(',');\n    return '''\n    $mPrefix $name($params, {$optional,}) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.between(\n          indexName: r'${index.name}',\n          lower: [$values $lowerName],\n          includeLower: includeLower,\n          upper: [$values $upperName],\n          includeUpper: includeUpper,\n          ${properties.containsFloat ? 'epsilon: epsilon,' : ''}\n        ));\n      });\n    }\n  ''';\n  }\n\n  String generateWhereIsNull(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'IsNull');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount - 1);\n    var values = joinToValues(properties);\n    if (values.isNotEmpty) {\n      values += ',';\n    }\n    final params = joinToParams(properties);\n    return '''\n    $mPrefix $name($params) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.equalTo(\n          indexName: r'${index.name}',\n          value: [$values null],\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateWhereIsNotNull(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'IsNotNull');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.takeFirst(propertyCount - 1);\n    var values = joinToValues(properties);\n    if (values.isNotEmpty) {\n      values += ',';\n    }\n    final params = joinToParams(properties);\n    return '''\n    $mPrefix $name($params) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.between(\n          indexName: r'${index.name}',\n          lower: [$values null],\n          includeLower: false,\n          upper: [$values],\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateWhereStartsWith(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'StartsWith');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final equalProperties = index.properties.dropLast(1);\n    var params = joinToParams(equalProperties);\n    if (params.isNotEmpty) {\n      params += ',';\n    }\n\n    final prefixProperty = index.properties.last;\n    final prefixName = '${paramName(prefixProperty).capitalize()}Prefix';\n    params += 'String $prefixName';\n    var values = joinToValues(equalProperties);\n    if (values.isNotEmpty) {\n      values += ',';\n    }\n\n    return '''\n    $mPrefix $name($params) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.between(\n          indexName: r'${index.name}',\n          lower: [$values $prefixName],\n          upper: [$values '\\$$prefixName\\\\u{FFFFF}'],\n        ));\n      });\n    }\n    ''';\n  }\n\n  String generateStringIsEmpty(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'IsEmpty');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.dropLast(1);\n    var values = joinToValues(properties);\n    if (values.isNotEmpty) {\n      values += ',';\n    }\n    final params = joinToParams(properties);\n\n    return '''\n    $mPrefix $name($params) {\n      return QueryBuilder.apply(this, (query) {\n        return query.addWhereClause(IndexWhereClause.equalTo(\n          indexName: r'${index.name}',\n          value: [$values ''],\n        ));\n      });\n    }''';\n  }\n\n  String generateStringIsNotEmpty(ObjectIndex index, int propertyCount) {\n    final name = getMethodName(index, propertyCount, 'IsNotEmpty');\n    if (!existing.add(name)) {\n      return '';\n    }\n\n    final properties = index.properties.dropLast(1);\n    var values = joinToValues(properties);\n    if (values.isNotEmpty) {\n      values += ',';\n    }\n    final params = joinToParams(properties);\n\n    return '''\n    $mPrefix $name($params) {\n      return QueryBuilder.apply(this, (query) {\n        if (query.whereSort == Sort.asc) {\n          return query.addWhereClause(IndexWhereClause.lessThan(\n            indexName: r'${index.name}',\n            upper: [''],\n          )).addWhereClause(IndexWhereClause.greaterThan(\n            indexName: r'${index.name}',\n            lower: [''],\n          ));\n        } else {\n          return query.addWhereClause(IndexWhereClause.greaterThan(\n            indexName: r'${index.name}',\n            lower: [''],\n          )).addWhereClause(IndexWhereClause.lessThan(\n            indexName: r'${index.name}',\n            upper: [''],\n          ));\n        }\n      });\n    }''';\n  }\n}\n\nextension on List<ObjectIndexProperty> {\n  bool get containsFloat =>\n      last.isarType == IsarType.float || last.isarType == IsarType.floatList;\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/code_gen/type_adapter_generator.dart",
    "content": "import 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nString _prepareSerialize(\n  bool nullable,\n  String value,\n  String Function(String) size,\n) {\n  var code = '';\n  if (nullable) {\n    code += '''\n      {\n        final value = $value;\n        if (value != null) {''';\n    value = 'value';\n  }\n  code += 'bytesCount += ${size(value)};';\n  if (nullable) {\n    code += '}}';\n  }\n  return code;\n}\n\nString _prepareSerializeList(\n  bool nullable,\n  bool elementNullable,\n  String value,\n  String size, [\n  String? prepare,\n]) {\n  var code = '';\n  if (nullable) {\n    code += '''\n      {\n        final list = $value;\n        if (list != null) {''';\n    value = 'list';\n  }\n  code += '''\n    bytesCount += 3 + $value.length * 3;\n    {\n      ${prepare ?? ''}\n      for (var i = 0; i < $value.length; i++) {\n        final value = $value[i];''';\n  if (elementNullable) {\n    code += 'if (value != null) {';\n  }\n  code += 'bytesCount += $size;';\n  if (elementNullable) {\n    code += '}';\n  }\n  code += '}}';\n  if (nullable) {\n    code += '}}';\n  }\n  return code;\n}\n\nString generateEstimateSerialize(ObjectInfo object) {\n  var code = '''\n    int ${object.estimateSizeName}(\n      ${object.dartName} object,\n      List<int> offsets,\n      Map<Type, List<int>> allOffsets,\n    ) {\n      var bytesCount = offsets.last;''';\n\n  for (final property in object.properties) {\n    final value = 'object.${property.dartName}';\n\n    switch (property.isarType) {\n      case IsarType.string:\n        final enumValue = property.isEnum ? '.${property.enumProperty}' : '';\n        code += _prepareSerialize(\n          property.nullable,\n          value,\n          (value) => '3 + $value$enumValue.length * 3',\n        );\n        break;\n\n      case IsarType.stringList:\n        final enumValue = property.isEnum ? '.${property.enumProperty}' : '';\n        code += _prepareSerializeList(\n          property.nullable,\n          property.elementNullable,\n          value,\n          'value$enumValue.length * 3',\n        );\n        break;\n\n      case IsarType.object:\n        code += _prepareSerialize(\n          property.nullable,\n          value,\n          (value) {\n            return '3 + ${property.targetSchema}.estimateSize($value, '\n                'allOffsets[${property.scalarDartType}]!, allOffsets)';\n          },\n        );\n        break;\n\n      case IsarType.objectList:\n        code += _prepareSerializeList(\n          property.nullable,\n          property.elementNullable,\n          value,\n          '${property.targetSchema}.estimateSize(value, offsets, allOffsets)',\n          'final offsets = allOffsets[${property.scalarDartType}]!;',\n        );\n        break;\n\n      case IsarType.byteList:\n      case IsarType.boolList:\n        code += _prepareSerialize(\n          property.nullable,\n          value,\n          (value) => '3 + $value.length',\n        );\n        break;\n      case IsarType.intList:\n      case IsarType.floatList:\n        code += _prepareSerialize(\n          property.nullable,\n          value,\n          (value) => '3 + $value.length * 4',\n        );\n        break;\n      case IsarType.longList:\n      case IsarType.doubleList:\n      case IsarType.dateTimeList:\n        code += _prepareSerialize(\n          property.nullable,\n          value,\n          (value) => '3 + $value.length * 8',\n        );\n        break;\n\n      // ignore: no_default_cases\n      default:\n        break;\n    }\n  }\n\n  return '''\n      $code\n      return bytesCount;\n    }''';\n}\n\nString generateSerialize(ObjectInfo object) {\n  var code = '''\n  void ${object.serializeName}(\n    ${object.dartName} object, \n    IsarWriter writer,\n    List<int> offsets, \n    Map<Type, List<int>> allOffsets,\n  ) {''';\n\n  for (var i = 0; i < object.objectProperties.length; i++) {\n    final property = object.objectProperties[i];\n    var value = 'object.${property.dartName}';\n    if (property.isEnum) {\n      final nOp = property.nullable ? '?' : '';\n      final elNOp = property.elementNullable ? '?' : '';\n      value = property.isarType.isList\n          ? '$value$nOp.map((e) => e$elNOp.${property.enumProperty}).toList()'\n          : '$value$nOp.${property.enumProperty}';\n    }\n\n    switch (property.isarType) {\n      case IsarType.bool:\n        code += 'writer.writeBool(offsets[$i], $value);';\n        break;\n      case IsarType.byte:\n        code += 'writer.writeByte(offsets[$i], $value);';\n        break;\n      case IsarType.int:\n        code += 'writer.writeInt(offsets[$i], $value);';\n        break;\n      case IsarType.float:\n        code += 'writer.writeFloat(offsets[$i], $value);';\n        break;\n      case IsarType.long:\n        code += 'writer.writeLong(offsets[$i], $value);';\n        break;\n      case IsarType.double:\n        code += 'writer.writeDouble(offsets[$i], $value);';\n        break;\n      case IsarType.dateTime:\n        code += 'writer.writeDateTime(offsets[$i], $value);';\n        break;\n      case IsarType.string:\n        code += 'writer.writeString(offsets[$i], $value);';\n        break;\n      case IsarType.object:\n        code += '''\n          writer.writeObject<${property.typeClassName}>(\n            offsets[$i],\n            allOffsets,\n            ${property.targetSchema}.serialize,\n            $value,\n          );''';\n        break;\n      case IsarType.byteList:\n        code += 'writer.writeByteList(offsets[$i], $value);';\n        break;\n      case IsarType.boolList:\n        code += 'writer.writeBoolList(offsets[$i], $value);';\n        break;\n      case IsarType.intList:\n        code += 'writer.writeIntList(offsets[$i], $value);';\n        break;\n      case IsarType.longList:\n        code += 'writer.writeLongList(offsets[$i], $value);';\n        break;\n      case IsarType.floatList:\n        code += 'writer.writeFloatList(offsets[$i], $value);';\n        break;\n      case IsarType.doubleList:\n        code += 'writer.writeDoubleList(offsets[$i], $value);';\n        break;\n      case IsarType.dateTimeList:\n        code += 'writer.writeDateTimeList(offsets[$i], $value);';\n        break;\n      case IsarType.stringList:\n        code += 'writer.writeStringList(offsets[$i], $value);';\n        break;\n      case IsarType.objectList:\n        code += '''\n          writer.writeObjectList<${property.typeClassName}>(\n            offsets[$i],\n            allOffsets,\n            ${property.targetSchema}.serialize,\n            $value,\n          );''';\n        break;\n    }\n  }\n\n  return '$code}';\n}\n\nString generateDeserialize(ObjectInfo object) {\n  var code = '''\n    ${object.dartName} ${object.deserializeName}(\n      Id id,\n      IsarReader reader,\n      List<int> offsets,\n      Map<Type, List<int>> allOffsets,\n    ) {\n      final object = ${object.dartName}(''';\n\n  final propertiesByMode =\n      object.properties.groupBy((ObjectProperty p) => p.deserialize);\n  final positional = propertiesByMode[PropertyDeser.positionalParam] ?? [];\n  final sortedPositional =\n      positional.sortedBy((ObjectProperty p) => p.constructorPosition!);\n  for (final p in sortedPositional) {\n    final index = object.objectProperties.indexOf(p);\n    final deser = _deserializeProperty(object, p, 'offsets[$index]');\n    code += '$deser,';\n  }\n\n  final named = propertiesByMode[PropertyDeser.namedParam] ?? [];\n  for (final p in named) {\n    final index = object.objectProperties.indexOf(p);\n    final deser = _deserializeProperty(object, p, 'offsets[$index]');\n    code += '${p.dartName}: $deser,';\n  }\n\n  code += ');';\n\n  final assign = propertiesByMode[PropertyDeser.assign] ?? [];\n  for (final p in assign) {\n    final index = object.objectProperties.indexOf(p);\n    final deser = _deserializeProperty(object, p, 'offsets[$index]');\n    code += 'object.${p.dartName} = $deser;';\n  }\n\n  return '''\n    $code\n    return object;\n  }''';\n}\n\nString generateDeserializeProp(ObjectInfo object) {\n  var code = '''\n    P ${object.deserializePropName}<P>(\n      IsarReader reader,\n      int propertyId,\n      int offset,\n      Map<Type,\n      List<int>> allOffsets,\n    ) {\n      switch (propertyId) {''';\n\n  for (var i = 0; i < object.objectProperties.length; i++) {\n    final property = object.objectProperties[i];\n    final deser = _deserializeProperty(object, property, 'offset');\n    code += 'case $i: return ($deser) as P;';\n  }\n\n  return '''\n      $code\n      default:\n        throw IsarError('Unknown property with id \\$propertyId');\n      }\n    }\n    ''';\n}\n\nString _deserializeProperty(\n  ObjectInfo object,\n  ObjectProperty property,\n  String propertyOffset,\n) {\n  if (property.isId) {\n    return 'id';\n  }\n\n  final deser = _deserialize(property, propertyOffset);\n\n  var defaultValue = '';\n  if (!property.nullable) {\n    if (property.userDefaultValue != null) {\n      defaultValue = '?? ${property.userDefaultValue}';\n    } else if (property.isarType == IsarType.object) {\n      defaultValue = '?? ${property.typeClassName}()';\n    } else if (property.isarType.isList) {\n      defaultValue = '?? []';\n    } else if (property.isEnum) {\n      defaultValue = '?? ${property.defaultEnumElement}';\n    }\n  }\n\n  if (property.isEnum) {\n    if (property.isarType.isList) {\n      final elDefault =\n          !property.elementNullable ? '?? ${property.defaultEnumElement}' : '';\n      return '$deser?.map((e) => ${property.valueEnumMapName(object)}[e] '\n          '$elDefault).toList() $defaultValue';\n    } else {\n      return '${property.valueEnumMapName(object)}[$deser] $defaultValue';\n    }\n  } else {\n    return '$deser $defaultValue';\n  }\n}\n\nString _deserialize(ObjectProperty property, String propertyOffset) {\n  final orNull =\n      property.nullable || property.userDefaultValue != null || property.isEnum\n          ? 'OrNull'\n          : '';\n  final orElNull = property.elementNullable ? 'OrNull' : '';\n\n  switch (property.isarType) {\n    case IsarType.bool:\n      return 'reader.readBool$orNull($propertyOffset)';\n    case IsarType.byte:\n      return 'reader.readByte$orNull($propertyOffset)';\n    case IsarType.int:\n      return 'reader.readInt$orNull($propertyOffset)';\n    case IsarType.float:\n      return 'reader.readFloat$orNull($propertyOffset)';\n    case IsarType.long:\n      return 'reader.readLong$orNull($propertyOffset)';\n    case IsarType.double:\n      return 'reader.readDouble$orNull($propertyOffset)';\n    case IsarType.dateTime:\n      return 'reader.readDateTime$orNull($propertyOffset)';\n    case IsarType.string:\n      return 'reader.readString$orNull($propertyOffset)';\n    case IsarType.object:\n      return '''\n        reader.readObjectOrNull<${property.typeClassName}>(\n          $propertyOffset,\n          ${property.targetSchema}.deserialize,\n          allOffsets,\n        )''';\n    case IsarType.boolList:\n      return 'reader.readBool${orElNull}List($propertyOffset)';\n    case IsarType.byteList:\n      return 'reader.readByteList($propertyOffset)';\n    case IsarType.intList:\n      return 'reader.readInt${orElNull}List($propertyOffset)';\n    case IsarType.floatList:\n      return 'reader.readFloat${orElNull}List($propertyOffset)';\n    case IsarType.longList:\n      return 'reader.readLong${orElNull}List($propertyOffset)';\n    case IsarType.doubleList:\n      return 'reader.readDouble${orElNull}List($propertyOffset)';\n    case IsarType.dateTimeList:\n      return 'reader.readDateTime${orElNull}List($propertyOffset)';\n    case IsarType.stringList:\n      return 'reader.readString${orElNull}List($propertyOffset)';\n    case IsarType.objectList:\n      return '''\n        reader.readObject${orElNull}List<${property.typeClassName}>(\n          $propertyOffset,\n          ${property.targetSchema}.deserialize,\n          allOffsets,\n          ${!property.elementNullable ? '${property.typeClassName}(),' : ''}\n        )''';\n  }\n}\n\nString generateGetId(ObjectInfo object) {\n  final defaultVal = object.idProperty.nullable ? '?? Isar.autoIncrement' : '';\n  return '''\n    Id ${object.getIdName}(${object.dartName} object) {\n      return object.${object.idProperty.dartName} $defaultVal;\n    }\n  ''';\n}\n\nString generateGetLinks(ObjectInfo object) {\n  return '''\n    List<IsarLinkBase<dynamic>> ${object.getLinksName}(${object.dartName} object) {\n      return [${object.links.map((e) => 'object.${e.dartName}').join(',')}];\n    }\n  ''';\n}\n\nString generateAttach(ObjectInfo object) {\n  var code = '''\n  void ${object.attachName}(IsarCollection<dynamic> col, Id id, ${object.dartName} object) {''';\n\n  if (object.idProperty.assignable) {\n    code += 'object.${object.idProperty.dartName} = id;';\n  }\n\n  for (final link in object.links) {\n    // ignore: leading_newlines_in_multiline_strings\n    code += '''object.${link.dartName}.attach(\n      col,\n      col.isar.collection<${link.targetCollectionDartName}>(),\n      r'${link.isarName}',\n      id\n    );''';\n  }\n  return '$code}';\n}\n\nString generateEnumMaps(ObjectInfo object) {\n  var code = '';\n  for (final property in object.properties) {\n    final enumName = property.typeClassName;\n    if (property.isEnum) {\n      code += 'const ${property.enumValueMapName(object)} = {';\n      for (final enumElementName in property.enumMap!.keys) {\n        final value = property.enumMap![enumElementName];\n        if (value is String) {\n          code += \"r'$enumElementName': r'$value',\";\n        } else {\n          code += \"'$enumElementName': $value,\";\n        }\n      }\n      code += '};';\n\n      code += 'const ${property.valueEnumMapName(object)} = {';\n      for (final enumElementName in property.enumMap!.keys) {\n        final value = property.enumMap![enumElementName];\n        if (value is String) {\n          code += \"r'$value': $enumName.$enumElementName,\";\n        } else {\n          code += '$value: $enumName.$enumElementName,';\n        }\n      }\n      code += '};';\n    }\n  }\n\n  return code;\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/collection_generator.dart",
    "content": "import 'dart:async';\n\nimport 'package:analyzer/dart/element/element.dart';\nimport 'package:build/build.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/code_gen/by_index_generator.dart';\nimport 'package:isar_generator/src/code_gen/collection_schema_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_distinct_by_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_filter_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_link_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_object_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_property_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_sort_by_generator.dart';\nimport 'package:isar_generator/src/code_gen/query_where_generator.dart';\nimport 'package:isar_generator/src/code_gen/type_adapter_generator.dart';\nimport 'package:isar_generator/src/isar_analyzer.dart';\nimport 'package:source_gen/source_gen.dart';\n\nconst ignoreLints = [\n  'duplicate_ignore',\n  'non_constant_identifier_names',\n  'constant_identifier_names',\n  'invalid_use_of_protected_member',\n  'unnecessary_cast',\n  'prefer_const_constructors',\n  'lines_longer_than_80_chars',\n  'require_trailing_commas',\n  'inference_failure_on_function_invocation',\n  'unnecessary_parenthesis',\n  'unnecessary_raw_strings',\n  'unnecessary_null_checks',\n  'join_return_with_assignment',\n  'prefer_final_locals',\n  'avoid_js_rounded_ints',\n  'avoid_positional_boolean_parameters',\n  'always_specify_types',\n];\n\nclass IsarCollectionGenerator extends GeneratorForAnnotation<Collection> {\n  @override\n  Future<String> generateForAnnotatedElement(\n    Element element,\n    ConstantReader annotation,\n    BuildStep buildStep,\n  ) async {\n    final object = IsarAnalyzer().analyzeCollection(element);\n    return '''\n      // coverage:ignore-file\n      // ignore_for_file: ${ignoreLints.join(', ')}\n\n      extension Get${object.dartName}Collection on Isar {\n        IsarCollection<${object.dartName}> get ${object.accessor} => this.collection();\n      }\n\n      ${generateSchema(object)}\n\n      ${generateEstimateSerialize(object)}\n      ${generateSerialize(object)}\n      ${generateDeserialize(object)}\n      ${generateDeserializeProp(object)}\n\n      ${generateEnumMaps(object)}\n\n      ${generateGetId(object)}\n      ${generateGetLinks(object)}\n      ${generateAttach(object)}\n\n      ${generateByIndexExtension(object)}\n      ${WhereGenerator(object).generate()}\n      ${FilterGenerator(object).generate()}\n      ${generateQueryObjects(object)}\n      ${generateQueryLinks(object)}\n      ${generateSortBy(object)}\n      ${generateDistinctBy(object)}\n      ${generatePropertyQuery(object)}\n    ''';\n  }\n}\n\nclass IsarEmbeddedGenerator extends GeneratorForAnnotation<Embedded> {\n  @override\n  Future<String> generateForAnnotatedElement(\n    Element element,\n    ConstantReader annotation,\n    BuildStep buildStep,\n  ) async {\n    final object = IsarAnalyzer().analyzeEmbedded(element);\n    return '''\n      // coverage:ignore-file\n      // ignore_for_file: ${ignoreLints.join(', ')}\n\n      ${generateSchema(object)}\n\n      ${generateEstimateSerialize(object)}\n      ${generateSerialize(object)}\n      ${generateDeserialize(object)}\n      ${generateDeserializeProp(object)}\n\n      ${generateEnumMaps(object)}\n\n      ${FilterGenerator(object).generate()}\n      ${generateQueryObjects(object)}\n    ''';\n  }\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/helper.dart",
    "content": "import 'package:analyzer/dart/constant/value.dart';\nimport 'package:analyzer/dart/element/element.dart';\nimport 'package:analyzer/dart/element/type.dart';\nimport 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\nimport 'package:source_gen/source_gen.dart';\n\nconst TypeChecker _collectionChecker = TypeChecker.fromRuntime(Collection);\nconst TypeChecker _enumeratedChecker = TypeChecker.fromRuntime(Enumerated);\nconst TypeChecker _embeddedChecker = TypeChecker.fromRuntime(Embedded);\nconst TypeChecker _ignoreChecker = TypeChecker.fromRuntime(Ignore);\nconst TypeChecker _nameChecker = TypeChecker.fromRuntime(Name);\nconst TypeChecker _indexChecker = TypeChecker.fromRuntime(Index);\nconst TypeChecker _backlinkChecker = TypeChecker.fromRuntime(Backlink);\n\nextension ClassElementX on ClassElement {\n  bool get hasZeroArgsConstructor {\n    return constructors.any(\n      (ConstructorElement c) =>\n          c.isPublic &&\n          !c.parameters.any((ParameterElement p) => !p.isOptional),\n    );\n  }\n\n  List<PropertyInducingElement> get allAccessors {\n    final ignoreFields =\n        collectionAnnotation?.ignore ?? embeddedAnnotation!.ignore;\n    return [\n      ...accessors.mapNotNull((e) => e.variable),\n      if (collectionAnnotation?.inheritance ?? embeddedAnnotation!.inheritance)\n        for (InterfaceType supertype in allSupertypes) ...[\n          if (!supertype.isDartCoreObject)\n            ...supertype.accessors.mapNotNull((e) => e.variable)\n        ]\n    ]\n        .where(\n          (PropertyInducingElement e) =>\n              e.isPublic &&\n              !e.isStatic &&\n              !_ignoreChecker.hasAnnotationOf(e.nonSynthetic) &&\n              !ignoreFields.contains(e.name),\n        )\n        .distinctBy((e) => e.name)\n        .toList();\n  }\n\n  List<String> get enumConsts {\n    return fields.where((e) => e.isEnumConstant).map((e) => e.name).toList();\n  }\n}\n\nextension PropertyElementX on PropertyInducingElement {\n  bool get isLink => type.element2!.name == 'IsarLink';\n\n  bool get isLinks => type.element2!.name == 'IsarLinks';\n\n  Enumerated? get enumeratedAnnotation {\n    final ann = _enumeratedChecker.firstAnnotationOfExact(nonSynthetic);\n    if (ann == null) {\n      return null;\n    }\n    final typeIndex = ann.getField('type')!.getField('index')!.toIntValue()!;\n    return Enumerated(\n      EnumType.values[typeIndex],\n      ann.getField('property')?.toStringValue(),\n    );\n  }\n\n  Backlink? get backlinkAnnotation {\n    final ann = _backlinkChecker.firstAnnotationOfExact(nonSynthetic);\n    if (ann == null) {\n      return null;\n    }\n    return Backlink(to: ann.getField('to')!.toStringValue()!);\n  }\n\n  List<Index> get indexAnnotations {\n    return _indexChecker.annotationsOfExact(nonSynthetic).map((DartObject ann) {\n      final rawComposite = ann.getField('composite')!.toListValue();\n      final composite = <CompositeIndex>[];\n      if (rawComposite != null) {\n        for (final c in rawComposite) {\n          final indexTypeField = c.getField('type')!;\n          IndexType? indexType;\n          if (!indexTypeField.isNull) {\n            final indexTypeIndex =\n                indexTypeField.getField('index')!.toIntValue()!;\n            indexType = IndexType.values[indexTypeIndex];\n          }\n          composite.add(\n            CompositeIndex(\n              c.getField('property')!.toStringValue()!,\n              type: indexType,\n              caseSensitive: c.getField('caseSensitive')!.toBoolValue(),\n            ),\n          );\n        }\n      }\n      final indexTypeField = ann.getField('type')!;\n      IndexType? indexType;\n      if (!indexTypeField.isNull) {\n        final indexTypeIndex = indexTypeField.getField('index')!.toIntValue()!;\n        indexType = IndexType.values[indexTypeIndex];\n      }\n      return Index(\n        name: ann.getField('name')!.toStringValue(),\n        composite: composite,\n        unique: ann.getField('unique')!.toBoolValue()!,\n        replace: ann.getField('replace')!.toBoolValue()!,\n        type: indexType,\n        caseSensitive: ann.getField('caseSensitive')!.toBoolValue(),\n      );\n    }).toList();\n  }\n}\n\nextension ElementX on Element {\n  String get isarName {\n    final ann = _nameChecker.firstAnnotationOfExact(nonSynthetic);\n    late String name;\n    if (ann == null) {\n      name = displayName;\n    } else {\n      name = ann.getField('name')!.toStringValue()!;\n    }\n    checkIsarName(name, this);\n    return name;\n  }\n\n  Collection? get collectionAnnotation {\n    final ann = _collectionChecker.firstAnnotationOfExact(nonSynthetic);\n    if (ann == null) {\n      return null;\n    }\n    return Collection(\n      inheritance: ann.getField('inheritance')!.toBoolValue()!,\n      accessor: ann.getField('accessor')!.toStringValue(),\n      ignore: ann\n          .getField('ignore')!\n          .toSetValue()!\n          .map((e) => e.toStringValue()!)\n          .toSet(),\n    );\n  }\n\n  String get collectionAccessor {\n    var accessor = collectionAnnotation?.accessor;\n    if (accessor != null) {\n      return accessor;\n    }\n\n    accessor = displayName.decapitalize();\n    if (!accessor.endsWith('s')) {\n      accessor += 's';\n    }\n\n    return accessor;\n  }\n\n  Embedded? get embeddedAnnotation {\n    final ann = _embeddedChecker.firstAnnotationOfExact(nonSynthetic);\n    if (ann == null) {\n      return null;\n    }\n    return Embedded(\n      inheritance: ann.getField('inheritance')!.toBoolValue()!,\n      ignore: ann\n          .getField('ignore')!\n          .toSetValue()!\n          .map((e) => e.toStringValue()!)\n          .toSet(),\n    );\n  }\n}\n\nvoid checkIsarName(String name, Element element) {\n  if (name.isBlank || name.startsWith('_')) {\n    err('Names must not be blank or start with \"_\".', element);\n  }\n}\n\nNever err(String msg, [Element? element]) {\n  throw InvalidGenerationSourceError(msg, element: element);\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/isar_analyzer.dart",
    "content": "import 'package:analyzer/dart/element/element.dart';\nimport 'package:analyzer/dart/element/nullability_suffix.dart';\nimport 'package:analyzer/dart/element/type.dart';\nimport 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\n\nimport 'package:isar_generator/src/helper.dart';\nimport 'package:isar_generator/src/isar_type.dart';\nimport 'package:isar_generator/src/object_info.dart';\n\nclass IsarAnalyzer {\n  ObjectInfo analyzeCollection(Element element) {\n    final constructor = _checkValidClass(element);\n    final modelClass = element as ClassElement;\n\n    final properties = <ObjectProperty>[];\n    final links = <ObjectLink>[];\n    for (final propertyElement in modelClass.allAccessors) {\n      if (propertyElement.isLink || propertyElement.isLinks) {\n        final link = analyzeObjectLink(propertyElement);\n        links.add(link);\n      } else {\n        final property = analyzeObjectProperty(propertyElement, constructor);\n        properties.add(property);\n      }\n    }\n    _checkValidPropertiesConstructor(properties, constructor);\n    if (links.map((e) => e.isarName).distinct().length != links.length) {\n      err('Two or more links have the same name.', modelClass);\n    }\n\n    final indexes = <ObjectIndex>[];\n    for (final propertyElement in modelClass.allAccessors) {\n      indexes.addAll(analyzeObjectIndex(properties, propertyElement));\n    }\n    if (indexes.map((e) => e.name).distinct().length != indexes.length) {\n      err('Two or more indexes have the same name.', modelClass);\n    }\n\n    final idProperties = properties.where((it) => it.isId);\n    if (idProperties.isEmpty) {\n      err(\n        'No id property defined. Use the \"Id\" type for your id property.',\n        modelClass,\n      );\n    } else if (idProperties.length > 1) {\n      err('Two or more properties with type \"Id\" defined.', modelClass);\n    }\n\n    return ObjectInfo(\n      dartName: modelClass.displayName,\n      isarName: modelClass.isarName,\n      accessor: modelClass.collectionAccessor,\n      properties: properties,\n      embeddedDartNames: _getEmbeddedDartNames(element),\n      indexes: indexes,\n      links: links,\n    );\n  }\n\n  ObjectInfo analyzeEmbedded(Element element) {\n    final constructor = _checkValidClass(element);\n    final modelClass = element as ClassElement;\n\n    if (constructor.parameters.any((e) => e.isRequired)) {\n      err(\n        'Constructors of embedded objects must not have required parameters.',\n        constructor,\n      );\n    }\n\n    final properties = <ObjectProperty>[];\n    for (final propertyElement in modelClass.allAccessors) {\n      if (propertyElement.isLink || propertyElement.isLinks) {\n        err('Embedded objects must not contain links', propertyElement);\n      } else {\n        final property = analyzeObjectProperty(propertyElement, constructor);\n        properties.add(property);\n      }\n    }\n    _checkValidPropertiesConstructor(properties, constructor);\n\n    final hasIndex = modelClass.allAccessors.any(\n      (it) => it.indexAnnotations.isNotEmpty,\n    );\n    if (hasIndex) {\n      err('Embedded objects must not have indexes.', modelClass);\n    }\n\n    final hasIdProperty = properties.any((it) => it.isId);\n    if (hasIdProperty) {\n      err('Embedded objects must not define an id.', modelClass);\n    }\n\n    return ObjectInfo(\n      dartName: modelClass.displayName,\n      isarName: modelClass.isarName,\n      properties: properties,\n    );\n  }\n\n  ConstructorElement _checkValidClass(Element modelClass) {\n    if (modelClass is! ClassElement ||\n        modelClass is EnumElement ||\n        modelClass is MixinElement) {\n      err(\n        'Only classes may be annotated with @Collection or @Embedded.',\n        modelClass,\n      );\n    }\n\n    if (modelClass.isAbstract) {\n      err('Class must not be abstract.', modelClass);\n    }\n\n    if (!modelClass.isPublic) {\n      err('Class must be public.', modelClass);\n    }\n\n    final constructor = modelClass.constructors\n        .firstOrNullWhere((ConstructorElement c) => c.periodOffset == null);\n    if (constructor == null) {\n      err('Class needs an unnamed constructor.', modelClass);\n    }\n\n    final hasCollectionSupertype = modelClass.allSupertypes.any((type) {\n      return type.element.collectionAnnotation != null ||\n          type.element.embeddedAnnotation != null;\n    });\n    if (hasCollectionSupertype) {\n      err(\n        'Class must not have a supertype annotated with @Collection or '\n        '@Embedded.',\n        modelClass,\n      );\n    }\n\n    return constructor;\n  }\n\n  void _checkValidPropertiesConstructor(\n    List<ObjectProperty> properties,\n    ConstructorElement constructor,\n  ) {\n    if (properties.map((e) => e.isarName).distinct().length !=\n        properties.length) {\n      err(\n        'Two or more properties have the same name.',\n        constructor.enclosingElement,\n      );\n    }\n\n    final unknownConstructorParameter = constructor.parameters.firstOrNullWhere(\n      (p) => p.isRequired && properties.none((e) => e.dartName == p.name),\n    );\n    if (unknownConstructorParameter != null) {\n      err(\n        'Constructor parameter does not match a property.',\n        unknownConstructorParameter,\n      );\n    }\n  }\n\n  Map<String, String> _getEmbeddedDartNames(ClassElement element) {\n    void _fillNames(Map<String, String> names, ClassElement element) {\n      for (final property in element.allAccessors) {\n        final type = property.type.scalarType.element;\n        if (type is ClassElement && type.embeddedAnnotation != null) {\n          final isarName = type.isarName;\n          if (!names.containsKey(isarName)) {\n            names[type.isarName] = type.displayName;\n            _fillNames(names, type);\n          }\n        }\n      }\n    }\n\n    final names = <String, String>{};\n    _fillNames(names, element);\n    return names;\n  }\n\n  ObjectProperty analyzeObjectProperty(\n    PropertyInducingElement property,\n    ConstructorElement constructor,\n  ) {\n    final dartType = property.type;\n    final scalarDartType = dartType.scalarType;\n    Map<String, dynamic>? enumMap;\n    String? enumPropertyName;\n    String? defaultEnumElement;\n\n    late final IsarType isarType;\n    if (scalarDartType.element is EnumElement) {\n      final enumeratedAnn = property.enumeratedAnnotation;\n      if (enumeratedAnn == null) {\n        err('Enum property must be annotated with @enumerated.', property);\n      }\n\n      final enumClass = scalarDartType.element! as EnumElement;\n      final enumElements =\n          enumClass.fields.where((f) => f.isEnumConstant).toList();\n      defaultEnumElement = '${enumClass.name}.${enumElements.first.name}';\n\n      if (enumeratedAnn.type == EnumType.ordinal) {\n        isarType = dartType.isDartCoreList ? IsarType.byteList : IsarType.byte;\n        enumMap = {\n          for (var i = 0; i < enumElements.length; i++) enumElements[i].name: i,\n        };\n        enumPropertyName = 'index';\n      } else if (enumeratedAnn.type == EnumType.ordinal32) {\n        isarType = dartType.isDartCoreList ? IsarType.intList : IsarType.int;\n\n        enumMap = {\n          for (var i = 0; i < enumElements.length; i++) enumElements[i].name: i,\n        };\n        enumPropertyName = 'index';\n      } else if (enumeratedAnn.type == EnumType.name) {\n        isarType =\n            dartType.isDartCoreList ? IsarType.stringList : IsarType.string;\n        enumMap = {\n          for (final value in enumElements) value.name: value.name,\n        };\n        enumPropertyName = 'name';\n      } else {\n        enumPropertyName = enumeratedAnn.property;\n        if (enumPropertyName == null) {\n          err(\n            'Enums with type EnumType.value must specify which property '\n            'should be used.',\n            property,\n          );\n        }\n        final enumProperty = enumClass.getField(enumPropertyName);\n        if (enumProperty == null || enumProperty.isEnumConstant) {\n          err('Enum property \"$enumProperty\" does not exist.', property);\n        } else if (enumProperty.nonSynthetic is PropertyAccessorElement) {\n          err('Only fields are supported for enum properties', enumProperty);\n        }\n\n        final enumIsarType = enumProperty.type.isarType;\n        if (enumIsarType != IsarType.byte &&\n            enumIsarType != IsarType.int &&\n            enumIsarType != IsarType.long &&\n            enumIsarType != IsarType.string) {\n          err('Unsupported enum property type.', enumProperty);\n        }\n\n        isarType =\n            dartType.isDartCoreList ? enumIsarType!.listType : enumIsarType!;\n        enumMap = {};\n        for (final element in enumElements) {\n          final property =\n              element.computeConstantValue()!.getField(enumPropertyName)!;\n          final propertyValue = property.toBoolValue() ??\n              property.toIntValue() ??\n              property.toDoubleValue() ??\n              property.toStringValue();\n          if (propertyValue == null) {\n            err(\n              'Null values are not supported for enum properties.',\n              enumProperty,\n            );\n          }\n\n          if (enumMap.values.contains(propertyValue)) {\n            err(\n              'Enum property has duplicate values.',\n              enumProperty,\n            );\n          }\n          enumMap[element.name] = propertyValue;\n        }\n      }\n    } else {\n      if (dartType.isarType != null) {\n        isarType = dartType.isarType!;\n      } else {\n        err(\n          'Unsupported type. Please annotate the property with @ignore.',\n          property,\n        );\n      }\n    }\n\n    final nullable = dartType.nullabilitySuffix != NullabilitySuffix.none;\n    final elementNullable = isarType.isList &&\n        dartType.scalarType.nullabilitySuffix != NullabilitySuffix.none;\n\n    if ((isarType == IsarType.byte && nullable) ||\n        (isarType == IsarType.byteList && elementNullable)) {\n      err('Bytes must not be nullable.', property);\n    }\n\n    final constructorParameter =\n        constructor.parameters.firstOrNullWhere((p) => p.name == property.name);\n    int? constructorPosition;\n    late PropertyDeser deserialize;\n    if (constructorParameter != null) {\n      if (constructorParameter.type != property.type) {\n        err(\n          'Constructor parameter type does not match property type',\n          constructorParameter,\n        );\n      }\n      deserialize = constructorParameter.isNamed\n          ? PropertyDeser.namedParam\n          : PropertyDeser.positionalParam;\n      constructorPosition =\n          constructor.parameters.indexOf(constructorParameter);\n    } else {\n      deserialize =\n          property.setter == null ? PropertyDeser.none : PropertyDeser.assign;\n    }\n\n    return ObjectProperty(\n      dartName: property.displayName,\n      isarName: property.isarName,\n      typeClassName: dartType.scalarType.element!.name!,\n      targetIsarName: isarType.containsObject\n          ? dartType.scalarType.element!.isarName\n          : null,\n      isarType: isarType,\n      isId: dartType.isIsarId,\n      enumMap: enumMap,\n      enumProperty: enumPropertyName,\n      defaultEnumElement: defaultEnumElement,\n      nullable: nullable,\n      elementNullable: elementNullable,\n      userDefaultValue: constructorParameter?.defaultValueCode,\n      deserialize: deserialize,\n      assignable: property.setter != null,\n      constructorPosition: constructorPosition,\n    );\n  }\n\n  ObjectLink analyzeObjectLink(PropertyInducingElement property) {\n    if (property.type.nullabilitySuffix != NullabilitySuffix.none) {\n      err('Link properties must not be nullable.', property);\n    } else if (property.isLate) {\n      err('Link properties must not be late.', property);\n    }\n\n    final type = property.type as ParameterizedType;\n    final linkType = type.typeArguments[0];\n    if (linkType.nullabilitySuffix != NullabilitySuffix.none) {\n      err('Links type must not be nullable.', property);\n    }\n\n    final targetCol = linkType.element! as ClassElement;\n    if (targetCol.collectionAnnotation == null) {\n      err('Link target is not annotated with @collection');\n    }\n\n    final backlinkAnn = property.backlinkAnnotation;\n    String? targetLinkIsarName;\n    if (backlinkAnn != null) {\n      final targetProperty = targetCol.allAccessors\n          .firstOrNullWhere((e) => e.displayName == backlinkAnn.to);\n      if (targetProperty == null) {\n        err('Target of Backlink does not exist', property);\n      } else if (targetProperty.backlinkAnnotation != null) {\n        err('Target of Backlink is also a backlink', property);\n      }\n\n      if (!targetProperty.isLink && !targetProperty.isLinks) {\n        err('Target of backlink is not a link', property);\n      }\n\n      final targetLink = analyzeObjectLink(targetProperty);\n      targetLinkIsarName = targetLink.isarName;\n    }\n\n    return ObjectLink(\n      dartName: property.displayName,\n      isarName: property.isarName,\n      targetLinkIsarName: targetLinkIsarName,\n      targetCollectionDartName: linkType.element!.name!,\n      targetCollectionIsarName: targetCol.isarName,\n      isSingle: property.isLink,\n    );\n  }\n\n  Iterable<ObjectIndex> analyzeObjectIndex(\n    List<ObjectProperty> properties,\n    PropertyInducingElement element,\n  ) sync* {\n    final property =\n        properties.firstOrNullWhere((it) => it.dartName == element.name);\n    if (property == null || property.isId) {\n      return;\n    }\n\n    for (final index in element.indexAnnotations) {\n      final indexProperties = <ObjectIndexProperty>[];\n      final isString = property.isarType == IsarType.string ||\n          property.isarType == IsarType.stringList;\n      final defaultType = property.isarType.isList || isString\n          ? IndexType.hash\n          : IndexType.value;\n\n      indexProperties.add(\n        ObjectIndexProperty(\n          property: property,\n          type: index.type ?? defaultType,\n          caseSensitive: index.caseSensitive ?? isString,\n        ),\n      );\n      for (final c in index.composite) {\n        final compositeProperty =\n            properties.firstOrNullWhere((it) => it.dartName == c.property);\n        if (compositeProperty == null) {\n          err('Property does not exist: \"${c.property}\".', element);\n        } else if (compositeProperty.isId) {\n          err('Ids cannot be indexed', element);\n        } else {\n          final isString = compositeProperty.isarType == IsarType.string ||\n              compositeProperty.isarType == IsarType.stringList;\n          final defaultType = compositeProperty.isarType.isList || isString\n              ? IndexType.hash\n              : IndexType.value;\n          indexProperties.add(\n            ObjectIndexProperty(\n              property: compositeProperty,\n              type: c.type ?? defaultType,\n              caseSensitive: c.caseSensitive ?? isString,\n            ),\n          );\n        }\n      }\n\n      final name = index.name ??\n          indexProperties.map((e) => e.property.isarName).join('_');\n      checkIsarName(name, element);\n\n      final objectIndex = ObjectIndex(\n        name: name,\n        properties: indexProperties,\n        unique: index.unique,\n        replace: index.replace,\n      );\n      _verifyObjectIndex(objectIndex, element);\n\n      yield objectIndex;\n    }\n  }\n\n  void _verifyObjectIndex(ObjectIndex index, Element element) {\n    final properties = index.properties;\n\n    if (properties.map((it) => it.property.isarName).distinct().length !=\n        properties.length) {\n      err('Composite index contains duplicate properties.', element);\n    }\n\n    for (var i = 0; i < properties.length; i++) {\n      final property = properties[i];\n      if (property.isarType.isList &&\n          property.type != IndexType.hash &&\n          properties.length > 1) {\n        err('Composite indexes do not support non-hashed lists.', element);\n      }\n      if (property.isarType.containsFloat && i != properties.lastIndex) {\n        err(\n          'Only the last property of a composite index may be a '\n          'double value.',\n          element,\n        );\n      }\n      if (property.isarType == IsarType.string) {\n        if (property.type != IndexType.hash && i != properties.lastIndex) {\n          err(\n            'Only the last property of a composite index may be a '\n            'non-hashed String.',\n            element,\n          );\n        }\n      }\n      if (property.isarType.containsObject) {\n        err(\n          'Embedded objects may not be indexed.',\n          element,\n        );\n      }\n      if (property.type != IndexType.value) {\n        if (!property.isarType.isList && property.isarType != IsarType.string) {\n          err('Only Strings and Lists may be hashed.', element);\n        } else if (property.isarType.containsFloat) {\n          err('List<double> may must not be hashed.', element);\n        }\n      }\n      if (property.isarType != IsarType.stringList &&\n          property.type == IndexType.hashElements) {\n        err('Only String lists may have hashed elements.', element);\n      }\n    }\n\n    if (!index.unique && index.replace) {\n      err('Only unique indexes can replace.', element);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/isar_type.dart",
    "content": "import 'package:analyzer/dart/element/element.dart';\nimport 'package:analyzer/dart/element/type.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_generator/src/helper.dart';\nimport 'package:source_gen/source_gen.dart';\n\nconst TypeChecker _dateTimeChecker = TypeChecker.fromRuntime(DateTime);\nbool _isDateTime(Element element) => _dateTimeChecker.isExactly(element);\n\nextension DartTypeX on DartType {\n  IsarType? get _primitiveIsarType {\n    if (isDartCoreBool) {\n      return IsarType.bool;\n    } else if (isDartCoreInt) {\n      if (alias?.element.name == 'byte') {\n        return IsarType.byte;\n      } else if (alias?.element.name == 'short') {\n        return IsarType.int;\n      } else {\n        return IsarType.long;\n      }\n    } else if (isDartCoreDouble) {\n      if (alias?.element.name == 'float') {\n        return IsarType.float;\n      } else {\n        return IsarType.double;\n      }\n    } else if (isDartCoreString) {\n      return IsarType.string;\n    } else if (_isDateTime(element2!)) {\n      return IsarType.dateTime;\n    } else if (element2!.embeddedAnnotation != null) {\n      return IsarType.object;\n    }\n\n    return null;\n  }\n\n  bool get isIsarId {\n    return alias?.element.name == 'Id';\n  }\n\n  DartType get scalarType {\n    if (isDartCoreList) {\n      final parameterizedType = this as ParameterizedType;\n      final typeArguments = parameterizedType.typeArguments;\n      if (typeArguments.isNotEmpty) {\n        return typeArguments[0];\n      }\n    }\n    return this;\n  }\n\n  IsarType? get isarType {\n    final primitiveType = _primitiveIsarType;\n    if (primitiveType != null) {\n      return primitiveType;\n    }\n\n    if (isDartCoreList) {\n      switch (scalarType._primitiveIsarType) {\n        case IsarType.bool:\n          return IsarType.boolList;\n        case IsarType.byte:\n          return IsarType.byteList;\n        case IsarType.int:\n          return IsarType.intList;\n        case IsarType.float:\n          return IsarType.floatList;\n        case IsarType.long:\n          return IsarType.longList;\n        case IsarType.double:\n          return IsarType.doubleList;\n        case IsarType.dateTime:\n          return IsarType.dateTimeList;\n        case IsarType.string:\n          return IsarType.stringList;\n        case IsarType.object:\n          return IsarType.objectList;\n        // ignore: no_default_cases\n        default:\n          return null;\n      }\n    }\n\n    return null;\n  }\n}\n\nextension IsarTypeX on IsarType {\n  bool get containsBool => this == IsarType.bool || this == IsarType.boolList;\n\n  bool get containsFloat =>\n      this == IsarType.float ||\n      this == IsarType.floatList ||\n      this == IsarType.double ||\n      this == IsarType.doubleList;\n\n  bool get containsDate =>\n      this == IsarType.dateTime || this == IsarType.dateTimeList;\n\n  bool get containsString =>\n      this == IsarType.string || this == IsarType.stringList;\n\n  bool get containsObject =>\n      this == IsarType.object || this == IsarType.objectList;\n}\n"
  },
  {
    "path": "packages/isar_generator/lib/src/object_info.dart",
    "content": "import 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:dartx/dartx.dart';\nimport 'package:isar/isar.dart';\n\nimport 'package:xxh3/xxh3.dart';\n\nclass ObjectInfo {\n  ObjectInfo({\n    required this.dartName,\n    required this.isarName,\n    this.accessor,\n    required List<ObjectProperty> properties,\n    this.embeddedDartNames = const {},\n    this.indexes = const [],\n    this.links = const [],\n  }) {\n    this.properties = properties.sortedBy((e) => e.isarName).toList();\n  }\n\n  final String dartName;\n  final String isarName;\n  final String? accessor;\n  late final List<ObjectProperty> properties;\n  final Map<String, String> embeddedDartNames;\n  final List<ObjectIndex> indexes;\n  final List<ObjectLink> links;\n\n  int get id => xxh3(utf8.encode(isarName) as Uint8List);\n\n  bool get isEmbedded => accessor == null;\n\n  ObjectProperty get idProperty => properties.firstWhere((it) => it.isId);\n\n  List<ObjectProperty> get objectProperties =>\n      properties.where((it) => !it.isId).toList();\n\n  String get getIdName => '_${dartName.decapitalize()}GetId';\n  String get getLinksName => '_${dartName.decapitalize()}GetLinks';\n  String get attachName => '_${dartName.decapitalize()}Attach';\n\n  String get estimateSizeName => '_${dartName.decapitalize()}EstimateSize';\n  String get serializeName => '_${dartName.decapitalize()}Serialize';\n  String get deserializeName => '_${dartName.decapitalize()}Deserialize';\n  String get deserializePropName =>\n      '_${dartName.decapitalize()}DeserializeProp';\n}\n\nenum PropertyDeser {\n  none,\n  assign,\n  positionalParam,\n  namedParam,\n}\n\nclass ObjectProperty {\n  ObjectProperty({\n    required this.dartName,\n    required this.isarName,\n    required this.typeClassName,\n    this.targetIsarName,\n    required this.isarType,\n    required this.isId,\n    required this.enumMap,\n    required this.enumProperty,\n    required this.defaultEnumElement,\n    required this.nullable,\n    required this.elementNullable,\n    this.userDefaultValue,\n    required this.deserialize,\n    required this.assignable,\n    this.constructorPosition,\n  });\n\n  final String dartName;\n  final String isarName;\n  final String typeClassName;\n  final String? targetIsarName;\n\n  final bool isId;\n  final IsarType isarType;\n  final Map<String, dynamic>? enumMap;\n  final String? enumProperty;\n  final String? defaultEnumElement;\n\n  final bool nullable;\n  final bool elementNullable;\n  final String? userDefaultValue;\n\n  final PropertyDeser deserialize;\n  final bool assignable;\n  final int? constructorPosition;\n\n  bool get isEnum => enumMap != null;\n\n  String get scalarDartType {\n    if (isId) {\n      return 'Id';\n    } else if (isEnum) {\n      return typeClassName;\n    }\n\n    switch (isarType) {\n      case IsarType.bool:\n      case IsarType.boolList:\n        return 'bool';\n      case IsarType.byte:\n      case IsarType.byteList:\n      case IsarType.int:\n      case IsarType.intList:\n      case IsarType.long:\n      case IsarType.longList:\n        return 'int';\n      case IsarType.float:\n      case IsarType.floatList:\n      case IsarType.double:\n      case IsarType.doubleList:\n        return 'double';\n      case IsarType.dateTime:\n      case IsarType.dateTimeList:\n        return 'DateTime';\n      case IsarType.object:\n      case IsarType.objectList:\n        return typeClassName;\n      case IsarType.string:\n      case IsarType.stringList:\n        return 'String';\n    }\n  }\n\n  String get nScalarDartType => isarType.isList\n      ? '$scalarDartType${elementNullable ? '?' : ''}'\n      : '$scalarDartType${nullable ? '?' : ''}';\n\n  String get dartType => isarType.isList\n      ? 'List<$nScalarDartType>${nullable ? '?' : ''}'\n      : nScalarDartType;\n\n  String get targetSchema => '${scalarDartType.capitalize()}Schema';\n\n  String enumValueMapName(ObjectInfo object) {\n    return '_${object.dartName}${dartName}EnumValueMap';\n  }\n\n  String valueEnumMapName(ObjectInfo object) {\n    return '_${object.dartName}${dartName}ValueEnumMap';\n  }\n}\n\nclass ObjectIndexProperty {\n  const ObjectIndexProperty({\n    required this.property,\n    required this.type,\n    required this.caseSensitive,\n  });\n\n  final ObjectProperty property;\n  final IndexType type;\n  final bool caseSensitive;\n\n  IsarType get isarType => property.isarType;\n\n  bool get isMultiEntry => isarType.isList && type != IndexType.hash;\n}\n\nclass ObjectIndex {\n  ObjectIndex({\n    required this.name,\n    required this.properties,\n    required this.unique,\n    required this.replace,\n  });\n\n  final String name;\n  final List<ObjectIndexProperty> properties;\n  final bool unique;\n  final bool replace;\n\n  late final id = xxh3(utf8.encode(name) as Uint8List);\n}\n\nclass ObjectLink {\n  const ObjectLink({\n    required this.dartName,\n    required this.isarName,\n    this.targetLinkIsarName,\n    required this.targetCollectionDartName,\n    required this.targetCollectionIsarName,\n    required this.isSingle,\n  });\n\n  final String dartName;\n  final String isarName;\n\n  // isar name of the original link (only for backlinks)\n  final String? targetLinkIsarName;\n  final String targetCollectionDartName;\n  final String targetCollectionIsarName;\n  final bool isSingle;\n\n  bool get isBacklink => targetLinkIsarName != null;\n\n  int id(String objectIsarName) {\n    final col = isBacklink ? targetCollectionIsarName : objectIsarName;\n    final colId = xxh3(utf8.encode(col) as Uint8List, seed: isBacklink ? 1 : 0);\n\n    final name = targetLinkIsarName ?? isarName;\n    return xxh3(utf8.encode(name) as Uint8List, seed: colId);\n  }\n}\n"
  },
  {
    "path": "packages/isar_generator/pubspec.yaml",
    "content": "name: isar_generator\ndescription: Code generator for the Isar Database. Finds classes annotated with @Collection.\nversion: 3.1.8\nrepository: https://github.com/isar-community/isar\nhomepage: https://isar.dev\npublish_to: https://pub.isar-community.dev/\n\nenvironment:\n  sdk: \">=2.17.0 <3.0.0\"\n\ndependencies:\n  analyzer: \">=4.6.0 <7.0.0\"\n  build: ^2.3.0\n  dart_style: ^2.2.3\n  dartx: ^1.1.0\n  glob: ^2.0.2\n  isar: \n    version: 3.1.8\n    hosted: https://pub.isar-community.dev\n  path: ^1.8.1\n  source_gen: ^1.2.2\n  xxh3: ^1.0.1\n\ndev_dependencies:\n  build_test: ^2.1.5\n  matcher: ^0.12.12\n  test: ^1.21.0\n  very_good_analysis: ^3.0.1\n"
  },
  {
    "path": "packages/isar_generator/pubspec_overrides.yaml",
    "content": "dependency_overrides:\n  isar:\n    path: ../isar\n"
  },
  {
    "path": "packages/isar_generator/test/error_test.dart",
    "content": "import 'dart:io';\n\nimport 'package:build/build.dart';\nimport 'package:build_test/build_test.dart';\nimport 'package:isar_generator/isar_generator.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('Error case', () {\n    for (final file in Directory('test/errors').listSync(recursive: true)) {\n      if (file is! File || !file.path.endsWith('.dart')) continue;\n\n      test(file.path, () async {\n        final content = await file.readAsLines();\n\n        final errorMessage = content.first.split('//').last.trim();\n\n        var error = '';\n        try {\n          await testBuilder(\n            getIsarGenerator(BuilderOptions.empty),\n            {'a|${file.path}': content.join('\\n')},\n            reader: await PackageAssetReader.currentIsolate(),\n          );\n        } catch (e) {\n          error = e.toString();\n        }\n\n        expect(error.toLowerCase(), contains(errorMessage.toLowerCase()));\n      });\n    }\n  });\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/abstract.dart",
    "content": "// must not be abstract\n\nimport 'package:isar/isar.dart';\n\n@collection\nabstract class Model {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/collection_supertype.dart",
    "content": "// supertype annotated with @collection\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Supertype {\n  Id? id;\n}\n\nclass Subtype implements Supertype {\n  @override\n  Id? id;\n}\n\n@collection\nclass Model implements Subtype {\n  @override\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/constructor_named.dart",
    "content": "// unnamed constructor\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Model.create();\n\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/constructor_unknown_parameter.dart",
    "content": "// constructor parameter does not match a property\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  // ignore: avoid_unused_constructor_parameters\n  Model(this.prop1, String somethingElse);\n\n  Id? id;\n\n  final String prop1;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/constructor_wrong_parameter.dart",
    "content": "// constructor parameter type does not match property type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  // ignore: avoid_unused_constructor_parameters\n  Model(int prop1);\n\n  Id? id;\n\n  String prop1 = '5';\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/enum.dart",
    "content": "// only classes\n\nimport 'package:isar/isar.dart';\n\n// ignore: invalid_annotation_target\n@collection\nenum Test { a, b, c }\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/invalid_name.dart",
    "content": "// must not be abstract\n\nimport 'package:isar/isar.dart';\n\n@collection\nabstract class Model {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/mixin.dart",
    "content": "// only classes\n\nimport 'package:isar/isar.dart';\n\n// ignore: invalid_annotation_target\n@collection\nmixin Test {}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/private.dart",
    "content": "// must be public\n\nimport 'package:isar/isar.dart';\n\n@collection\n// ignore: unused_element\nclass _Model {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/class/variable.dart",
    "content": "// only classes\n\nimport 'package:isar/isar.dart';\n\n// ignore: invalid_annotation_target\n@collection\nconst t = 'hello';\n"
  },
  {
    "path": "packages/isar_generator/test/errors/id/duplicate.dart",
    "content": "// two or more properties with type \"Id\" defined\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Test {\n  Id? id1;\n\n  Id? id2;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/id/missing.dart",
    "content": "// no id property defined\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Test {\n  late int id;\n\n  late String name;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/composite_double_not_last.dart",
    "content": "// only the last property of a composite index may be a double value\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(composite: [CompositeIndex('val2')])\n  double? val1;\n\n  String? val2;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/composite_non_hashed_list.dart",
    "content": "// composite indexes do not support non-hashed lists\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(composite: [CompositeIndex('str')], type: IndexType.value)\n  List<int>? list;\n\n  String? str;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/composite_string_value_not_last.dart",
    "content": "// last property of a composite index may be a non-hashed string\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(composite: [CompositeIndex('str2')], type: IndexType.value)\n  String? str1;\n\n  String? str2;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/contains_id.dart",
    "content": "// ids cannot be indexed\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(composite: [CompositeIndex('id')])\n  String? str;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/double_list_hashed.dart",
    "content": "// list<double> may must not be hashed\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(type: IndexType.hash)\n  List<double>? list;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/duplicate_name.dart",
    "content": "// same name\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(name: 'myindex')\n  String? prop1;\n\n  @Index(name: 'myindex')\n  String? prop2;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/duplicate_property.dart",
    "content": "// composite index contains duplicate properties\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(composite: [CompositeIndex('str1')], type: IndexType.value)\n  String? str1;\n\n  String? str2;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/invalid_name.dart",
    "content": "// names must not be blank or start with \"_\"\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(name: '_index')\n  String? str;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/non_string_hashed.dart",
    "content": "// only strings and lists may be hashed\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(type: IndexType.hash)\n  int? val;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/non_string_list_hashed_elements.dart",
    "content": "// only string lists may have hashed elements\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(type: IndexType.hashElements)\n  List<int>? list;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/non_unique_replace.dart",
    "content": "// only unique indexes can replace\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(replace: true)\n  String? str;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/object_hashed.dart",
    "content": "// objects may not be indexed\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index()\n  EmbeddedModel? obj;\n}\n\n@embedded\nclass EmbeddedModel {}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/object_list_hashed.dart",
    "content": "// objects may not be indexed\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(type: IndexType.hash)\n  List<EmbeddedModel>? list;\n}\n\n@embedded\nclass EmbeddedModel {}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/index/property_does_not_exist.dart",
    "content": "// property does not exist\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index(composite: [CompositeIndex('myProp')])\n  String? str;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/backlink_target_does_no_exist.dart",
    "content": "// target of backlink does not exist\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model1 {\n  Id? id;\n\n  @Backlink(to: 'abc')\n  final IsarLink<Model2> link = IsarLink();\n}\n\n@collection\nclass Model2 {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/backlink_target_is_backlink.dart",
    "content": "// target of backlink is also a backlink\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model1 {\n  Id? id;\n\n  @Backlink(to: 'link')\n  final IsarLink<Model2> link = IsarLink();\n}\n\n@collection\nclass Model2 {\n  Id? id;\n\n  @Backlink(to: 'link')\n  final IsarLink<Model1> link = IsarLink();\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/backlink_target_not_a_link.dart",
    "content": "// target of backlink is not a link\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model1 {\n  Id? id;\n\n  @Backlink(to: 'str')\n  final IsarLink<Model2> link = IsarLink();\n}\n\n@collection\nclass Model2 {\n  Id? id;\n\n  String? str;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/duplicate_name.dart",
    "content": "// same name\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  final IsarLink<Model2> prop1 = IsarLink();\n\n  @Name('prop1')\n  final IsarLinks<Model2> prop2 = IsarLinks();\n}\n\n@collection\nclass Model2 {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/invalid_name.dart",
    "content": "// names must not be blank or start with\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Name('_link')\n  final IsarLink<Model2> link = IsarLink();\n}\n\n@collection\nclass Model2 {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/late.dart",
    "content": "// must not be late\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  late IsarLink<Model2> link;\n}\n\n@collection\nclass Model2 {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/nullable.dart",
    "content": "// must not be nullable\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  IsarLink<Model2>? link;\n}\n\n@collection\nclass Model2 {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/target_not_a_collection.dart",
    "content": "// link target is not annotated with @collection\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  final IsarLink<int> link = IsarLink();\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/link/type_nullable.dart",
    "content": "// links type must not be nullable\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  final IsarLink<Model2?> link = IsarLink();\n}\n\n@collection\nclass Model2 {\n  Id? id;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/duplicate_name.dart",
    "content": "// same name\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  String? prop1;\n\n  @Name('prop1')\n  String? prop2;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_bool_type.dart",
    "content": "// unsupported enum property type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum field;\n}\n\nenum MyEnum {\n  optionA;\n\n  final bool value = true;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_double_type.dart",
    "content": "// unsupported enum property type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum field;\n}\n\nenum MyEnum {\n  optionA;\n\n  final double value = 5.5;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_duplicate.dart",
    "content": "// has duplicate values\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum field;\n}\n\nenum MyEnum {\n  option1(1),\n  option2(2),\n  option3(1);\n\n  const MyEnum(this.value);\n\n  final int value;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_float_type.dart",
    "content": "// unsupported enum property type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum field;\n}\n\nenum MyEnum {\n  optionA;\n\n  final float value = 5.5;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_list_type.dart",
    "content": "// unsupported enum property type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum prop;\n}\n\nenum MyEnum {\n  optionA;\n\n  final List<String> value = [];\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_not_annotated.dart",
    "content": "// enum property must be annotated with @enumerated\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  late MyEnum? prop;\n}\n\nenum MyEnum {\n  a;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_null_value.dart",
    "content": "// null values are not supported\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum prop;\n}\n\nenum MyEnum {\n  optionA;\n\n  final String? value = null;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/enum_object_type.dart",
    "content": "// unsupported enum property type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Enumerated(EnumType.value, 'value')\n  late MyEnum prop;\n}\n\nenum MyEnum {\n  optionA;\n\n  final value = EmbeddedModel();\n}\n\n@embedded\nclass EmbeddedModel {}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/invalid_name.dart",
    "content": "// names must not be blank or start with \"_\"\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Name('_prop')\n  String? prop;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/null_byte.dart",
    "content": "// bytes must not be nullable\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  late byte? prop;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/null_byte_element.dart",
    "content": "// bytes must not be nullable\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  late List<byte?> prop;\n}\n"
  },
  {
    "path": "packages/isar_generator/test/errors/property/unsupported_type.dart",
    "content": "// unsupported type\n\nimport 'package:isar/isar.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  late Set<String>? prop;\n}\n"
  },
  {
    "path": "packages/isar_inspector/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c\n      base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c\n    - platform: macos\n      create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c\n      base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "packages/isar_inspector/README.md",
    "content": "## Isar Inspector\n\n<img src=\"https://raw.githubusercontent.com/isar/isar/main/.github/assets/isar-inspector.png?sanitize=true\">\n"
  },
  {
    "path": "packages/isar_inspector/analysis_options.yaml",
    "content": "include: package:very_good_analysis/analysis_options.yaml\n\nanalyzer:\n  exclude:\n    - \"**/*.g.dart\"\n\n  errors:\n    cascade_invocations: ignore\n    avoid_positional_boolean_parameters: ignore\n    parameter_assignments: ignore\n    public_member_api_docs: ignore\n    use_string_buffers: ignore"
  },
  {
    "path": "packages/isar_inspector/isar_inspector.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/lib\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/test\" isTestSource=\"true\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.idea\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build\" />\n    </content>\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" name=\"Dart SDK\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Flutter Plugins\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Dart Packages\" level=\"project\" />\n  </component>\n</module>"
  },
  {
    "path": "packages/isar_inspector/lib/collection/button_prev_next.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar_inspector/collection/collection_area.dart';\n\nclass PrevNextButtons extends StatelessWidget {\n  const PrevNextButtons({\n    super.key,\n    required this.page,\n    required this.count,\n    required this.onChanged,\n  });\n\n  final int page;\n  final int count;\n  final void Function(int newPage) onChanged;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n\n    var from = 0;\n    var to = 0;\n    if (count > 0) {\n      from = page * objectsPerPage + 1;\n      to = from + objectsPerPage - 1;\n      if (to > count) {\n        to = count;\n      }\n    }\n\n    return Row(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        Tooltip(\n          message: 'Previous page',\n          child: TextButton(\n            onPressed: page > 0 ? () => onChanged(page - 1) : null,\n            child: const Text('Prev'),\n          ),\n        ),\n        const SizedBox(width: 10),\n        Tooltip(\n          message: 'Current page',\n          child: Row(\n            children: [\n              Text(\n                '$from',\n                style: const TextStyle(fontWeight: FontWeight.bold),\n              ),\n              Text(\n                ' - ',\n                style: TextStyle(\n                  color: theme.colorScheme.onBackground.withOpacity(0.7),\n                ),\n              ),\n              Text(\n                '$to',\n                style: const TextStyle(fontWeight: FontWeight.bold),\n              ),\n            ],\n          ),\n        ),\n        Text(\n          ' of ',\n          style: TextStyle(\n            color: theme.colorScheme.onBackground.withOpacity(0.7),\n          ),\n        ),\n        Tooltip(\n          message: 'Total number of objects',\n          child: Text(\n            '$count',\n            style: const TextStyle(fontWeight: FontWeight.bold),\n          ),\n        ),\n        const SizedBox(width: 10),\n        Tooltip(\n          message: 'Next page',\n          child: TextButton(\n            onPressed: to == count ? null : () => onChanged(page + 1),\n            child: const Text('Next'),\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/collection/button_sort.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\n\nclass SortButtons extends StatelessWidget {\n  const SortButtons({\n    super.key,\n    required this.properties,\n    required this.property,\n    required this.asc,\n    required this.onChanged,\n  });\n\n  final List<PropertySchema> properties;\n  final String property;\n  final bool asc;\n  final void Function(String property, bool asc) onChanged;\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        Tooltip(\n          message: 'Sort results by this property',\n          child: DropdownButtonHideUnderline(\n            child: DropdownButton<String>(\n              isDense: true,\n              items: [\n                for (final property in properties)\n                  if (property.type != IsarType.object && !property.type.isList)\n                    DropdownMenuItem(\n                      value: property.name,\n                      child: Text(property.name),\n                    ),\n              ],\n              value: property,\n              onChanged: (value) {\n                if (value != null) {\n                  onChanged(value, asc);\n                }\n              },\n            ),\n          ),\n        ),\n        const SizedBox(width: 10),\n        ActionChip(\n          label: Text(asc ? 'Asc' : 'Desc'),\n          onPressed: () {\n            onChanged(property, !asc);\n          },\n          tooltip: 'Toggle sort order',\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/collection/collection_area.dart",
    "content": "// ignore_for_file: type_annotate_public_apis, avoid_web_libraries_in_flutter\n\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:html';\nimport 'dart:math';\n\nimport 'package:clickup_fading_scroll/clickup_fading_scroll.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/collection/button_prev_next.dart';\nimport 'package:isar_inspector/collection/button_sort.dart';\nimport 'package:isar_inspector/collection/objects_list_sliver.dart';\nimport 'package:isar_inspector/connect_client.dart';\nimport 'package:isar_inspector/object/isar_object.dart';\nimport 'package:isar_inspector/query_builder/query_group.dart';\nimport 'package:isar_inspector/util.dart';\n\nconst objectsPerPage = 20;\n\nclass CollectionArea extends StatefulWidget {\n  const CollectionArea({\n    super.key,\n    required this.instance,\n    required this.collection,\n    required this.schemas,\n    required this.client,\n  });\n\n  final String instance;\n  final String collection;\n  final Map<String, Schema<dynamic>> schemas;\n  final ConnectClient client;\n\n  CollectionSchema<dynamic> get collectionSchema =>\n      schemas[collection]! as CollectionSchema;\n\n  @override\n  State<CollectionArea> createState() => _CollectionAreaState();\n}\n\nclass _CollectionAreaState extends State<CollectionArea> {\n  final controller = ScrollController();\n  late final StreamSubscription<void> querySubscription;\n\n  var page = 0;\n  var filter = const FilterGroup.and([]);\n  late var sortProperty = widget.collectionSchema.idName;\n  var sortAsc = true;\n  var objects = <IsarObject>[];\n  var objectsCount = 0;\n\n  @override\n  void initState() {\n    querySubscription = widget.client.queryChanged.listen((_) {\n      _runQuery();\n    });\n    _runQuery();\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    querySubscription.cancel();\n    super.dispose();\n  }\n\n  Future<void> _runQuery() async {\n    final query = ConnectQuery(\n      instance: widget.instance,\n      collection: widget.collection,\n      filter: filter,\n      offset: page * objectsPerPage,\n      limit: (page + 1) * objectsPerPage,\n      sortProperty: sortProperty,\n      sortAsc: sortAsc,\n    );\n    final result = await widget.client.executeQuery(query);\n    final objects = (result['objects']! as List)\n        .map((e) => IsarObject(e as Map<String, dynamic>))\n        .toList();\n\n    if (mounted) {\n      setState(() {\n        this.objects = objects;\n        objectsCount = result['count']! as int;\n      });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.stretch,\n      children: [\n        Expanded(\n          child: FadingScroll(\n            controller: controller,\n            builder: (context, controller) {\n              return CustomScrollView(\n                controller: controller,\n                slivers: [\n                  SliverToBoxAdapter(\n                    child: QueryGroup(\n                      collection: widget.collectionSchema,\n                      group: filter,\n                      level: 0,\n                      onChanged: (group) {\n                        setState(() {\n                          filter = group;\n                        });\n                        _runQuery();\n                      },\n                    ),\n                  ),\n                  ObjectsListSliver(\n                    instance: widget.instance,\n                    collection: widget.collection,\n                    schemas: widget.schemas,\n                    objects: objects,\n                    onUpdate: _onUpdate,\n                    onDelete: _onDelete,\n                  ),\n                ],\n              );\n            },\n          ),\n        ),\n        const SizedBox(height: 20),\n        Stack(\n          children: [\n            Positioned.fill(\n              child: Center(\n                child: PrevNextButtons(\n                  page: page,\n                  count: objectsCount,\n                  onChanged: (newPage) {\n                    setState(() {\n                      page = newPage;\n                    });\n                    _runQuery();\n                  },\n                ),\n              ),\n            ),\n            Row(\n              children: [\n                SortButtons(\n                  properties: widget.collectionSchema.idAndProperties,\n                  property: sortProperty,\n                  asc: sortAsc,\n                  onChanged: (property, asc) {\n                    setState(() {\n                      sortProperty = property;\n                      sortAsc = asc;\n                    });\n                    _runQuery();\n                  },\n                ),\n                const Spacer(),\n                Row(\n                  children: [\n                    IconButton(\n                      icon: Icon(\n                        Icons.add_rounded,\n                        color: theme.colorScheme.onBackground,\n                      ),\n                      iconSize: 26,\n                      tooltip: 'Create Object',\n                      onPressed: _onCreate,\n                    ),\n                    const SizedBox(width: 5),\n                    IconButton(\n                      icon: Icon(\n                        Icons.paste_rounded,\n                        color: theme.colorScheme.onBackground,\n                      ),\n                      iconSize: 20,\n                      tooltip: 'Import JSON from clipboard',\n                      onPressed: _onImport,\n                    ),\n                    const SizedBox(width: 5),\n                    IconButton(\n                      icon: Icon(\n                        Icons.download_rounded,\n                        color: theme.colorScheme.onBackground,\n                      ),\n                      tooltip: 'Download All',\n                      onPressed: _onDownload,\n                    ),\n                    const SizedBox(width: 5),\n                    IconButton(\n                      icon: Icon(\n                        Icons.delete_forever_rounded,\n                        color: theme.colorScheme.onBackground,\n                      ),\n                      tooltip: 'Delete All',\n                      onPressed: _onDeleteAll,\n                    ),\n                  ],\n                )\n              ],\n            ),\n          ],\n        )\n      ],\n    );\n  }\n\n  void _onUpdate(String collection, int id, String path, dynamic value) {\n    final edit = ConnectEdit(\n      instance: widget.instance,\n      collection: collection,\n      id: id,\n      path: path,\n      value: value,\n    );\n    widget.client.editProperty(edit);\n  }\n\n  void _onDelete(int id) {\n    final query = ConnectQuery(\n      instance: widget.instance,\n      collection: widget.collection,\n      filter: FilterCondition.equalTo(\n        property: widget.collectionSchema.idName,\n        value: id,\n      ),\n    );\n    widget.client.removeQuery(query);\n  }\n\n  Future<void> _onCreate() async {\n    final idName = widget.collectionSchema.idName;\n    final randomId = Random().nextInt(100000000);\n    await widget.client.importJson(\n      widget.instance,\n      widget.collection,\n      [\n        {idName: randomId}\n      ],\n    );\n    if (!mounted) return;\n\n    setState(() {\n      filter = FilterGroup.and([\n        FilterCondition.equalTo(\n          property: idName,\n          value: randomId,\n        ),\n      ]);\n    });\n    await _runQuery();\n  }\n\n  Future<void> _onImport() async {\n    try {\n      final jsonStr = await Clipboard.getData(Clipboard.kTextPlain);\n      var json = jsonDecode(jsonStr!.text!);\n      if (json is! List) {\n        json = [json];\n      }\n      await widget.client.importJson(widget.instance, widget.collection, json);\n    } on PlatformException {\n      ScaffoldMessenger.of(context).showSnackBar(\n        const SnackBar(content: Text('Could not access clipboard.')),\n      );\n    } on FormatException {\n      ScaffoldMessenger.of(context).showSnackBar(\n        const SnackBar(content: Text('Invalid JSON in clipboard.')),\n      );\n    }\n  }\n\n  void _onDeleteAll() {\n    final query = ConnectQuery(\n      instance: widget.instance,\n      collection: widget.collection,\n      filter: filter,\n    );\n    widget.client.removeQuery(query);\n  }\n\n  Future<void> _onDownload() async {\n    final query = ConnectQuery(\n      instance: widget.instance,\n      collection: widget.collection,\n      filter: filter,\n    );\n    final data = await widget.client.exportJson(query);\n    try {\n      final base64 = base64Encode(utf8.encode(jsonEncode(data)));\n      final anchor =\n          AnchorElement(href: 'data:application/octet-stream;base64,$base64')\n            ..target = 'blank'\n            ..download = '${widget.collection}.json';\n\n      document.body!.append(anchor);\n      anchor.click();\n      anchor.remove();\n    } catch (_) {}\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/collection/objects_list_sliver.dart",
    "content": "import 'dart:convert';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/object/isar_object.dart';\nimport 'package:isar_inspector/object/object_view.dart';\n\nclass ObjectsListSliver extends StatelessWidget {\n  const ObjectsListSliver({\n    super.key,\n    required this.instance,\n    required this.collection,\n    required this.schemas,\n    required this.objects,\n    required this.onUpdate,\n    required this.onDelete,\n  });\n\n  final String instance;\n  final String collection;\n  final Map<String, Schema<dynamic>> schemas;\n  final List<IsarObject> objects;\n  final void Function(\n    String collection,\n    int id,\n    String path,\n    dynamic value,\n  ) onUpdate;\n  final void Function(int id) onDelete;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final collectionSchema = schemas[collection]! as CollectionSchema;\n    return SliverList(\n      delegate: SliverChildBuilderDelegate(\n        childCount: objects.length,\n        (BuildContext context, int index) {\n          final object = objects[index];\n          return Card(\n            key: Key('object ${object.getValue(collectionSchema.idName)}'),\n            child: Padding(\n              padding: const EdgeInsets.all(5),\n              child: Stack(\n                children: [\n                  Padding(\n                    padding: const EdgeInsets.symmetric(vertical: 15),\n                    child: ObjectView(\n                      schemaName: collection,\n                      schemas: schemas,\n                      object: object,\n                      root: true,\n                      onUpdate: (collection, id, path, value) {\n                        onUpdate(collection, id!, path, value);\n                      },\n                    ),\n                  ),\n                  Positioned(\n                    top: 0,\n                    right: 0,\n                    child: Row(\n                      children: [\n                        IconButton(\n                          icon: Icon(\n                            Icons.copy_rounded,\n                            color: theme.colorScheme.onPrimaryContainer,\n                          ),\n                          tooltip: 'Copy as JSON',\n                          visualDensity: VisualDensity.standard,\n                          onPressed: () => _copyObject(object),\n                        ),\n                        IconButton(\n                          icon: Icon(\n                            Icons.delete_rounded,\n                            color: theme.colorScheme.onPrimaryContainer,\n                          ),\n                          tooltip: 'Delete',\n                          visualDensity: VisualDensity.standard,\n                          onPressed: () {\n                            final id = object.getValue(collectionSchema.idName);\n                            onDelete(id as int);\n                          },\n                        ),\n                      ],\n                    ),\n                  )\n                ],\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  void _copyObject(IsarObject object) {\n    final json = Map.of(object.data);\n    final schema = schemas[collection]! as CollectionSchema;\n    for (final linkName in schema.links.keys) {\n      json.remove(linkName);\n    }\n    Clipboard.setData(ClipboardData(text: jsonEncode(json)));\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/collections_list.dart",
    "content": "import 'dart:math';\n\nimport 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/connect_client.dart';\n\nclass CollectionsList extends StatelessWidget {\n  const CollectionsList({\n    super.key,\n    required this.collections,\n    required this.collectionInfo,\n    required this.selectedCollection,\n    required this.onSelected,\n  });\n\n  final List<CollectionSchema<dynamic>> collections;\n  final Map<String, ConnectCollectionInfo?> collectionInfo;\n  final String? selectedCollection;\n  final void Function(String collection) onSelected;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n\n    return ListView.builder(\n      primary: false,\n      itemBuilder: (BuildContext context, int index) {\n        final collection = collections[index];\n        final info = collectionInfo[collection.name];\n\n        return Padding(\n          padding: const EdgeInsets.only(bottom: 10),\n          child: ElevatedButton(\n            style: collection.name == selectedCollection\n                ? ElevatedButton.styleFrom(\n                    backgroundColor: theme.colorScheme.primaryContainer,\n                    foregroundColor: theme.colorScheme.onPrimaryContainer,\n                  )\n                : null,\n            onPressed: () {\n              onSelected(collection.name);\n            },\n            child: Padding(\n              padding: const EdgeInsets.only(\n                left: 25,\n                right: 10,\n                top: 12,\n                bottom: 12,\n              ),\n              child: Row(\n                children: [\n                  Expanded(\n                    child: Text(\n                      collection.name,\n                      style: const TextStyle(\n                        fontWeight: FontWeight.bold,\n                        fontSize: 16,\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ),\n                  ),\n                  Column(\n                    crossAxisAlignment: CrossAxisAlignment.end,\n                    mainAxisAlignment: MainAxisAlignment.center,\n                    children: [\n                      Text(\n                        info?.count.toString() ?? 'loading',\n                        style: const TextStyle(\n                          fontSize: 12,\n                        ),\n                      ),\n                      const SizedBox(height: 2),\n                      Text(\n                        _formatSize(info?.size ?? 0),\n                        style: const TextStyle(\n                          fontSize: 12,\n                        ),\n                      ),\n                    ],\n                  ),\n                ],\n              ),\n            ),\n          ),\n        );\n      },\n      itemCount: collections.length,\n    );\n  }\n}\n\nString _formatSize(int bytes) {\n  if (bytes <= 0) return '0 B';\n  const suffixes = ['B', 'KB', 'MB', 'GB'];\n  final n = (log(bytes) / log(1024)).floor();\n  final index = min(n, suffixes.length - 1);\n  final value = bytes / pow(1024, index);\n  return '${value.toStringAsFixed(index == 0 ? 0 : 2)} ${suffixes[index]}';\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/connect_client.dart",
    "content": "// ignore_for_file: implementation_imports\n\nimport 'dart:async';\nimport 'dart:convert';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar/src/isar_connect_api.dart';\nimport 'package:vm_service/vm_service.dart';\nimport 'package:web_socket_channel/web_socket_channel.dart';\n\nexport 'package:isar/src/isar_connect_api.dart';\n\nclass ConnectClient {\n  ConnectClient(this.vmService, this.isolateId);\n\n  static const Duration kNormalTimeout = Duration(seconds: 4);\n  static const Duration kLongTimeout = Duration(seconds: 10);\n\n  final VmService vmService;\n  final String isolateId;\n\n  final collectionInfo = <String, ConnectCollectionInfo>{};\n\n  final _instancesChangedController = StreamController<void>.broadcast();\n  final _collectionInfoChangedController = StreamController<void>.broadcast();\n  final _queryChangedController = StreamController<void>.broadcast();\n\n  Stream<void> get instancesChanged => _instancesChangedController.stream;\n  Stream<void> get collectionInfoChanged =>\n      _collectionInfoChangedController.stream;\n  Stream<void> get queryChanged => _queryChangedController.stream;\n\n  static Future<ConnectClient> connect(String port, String secret) async {\n    final wsUrl = Uri.parse('ws://127.0.0.1:$port/$secret=/ws');\n    final channel = WebSocketChannel.connect(wsUrl);\n\n    // ignore: avoid_print\n    final stream = channel.stream.handleError(print);\n\n    final service = VmService(\n      stream,\n      channel.sink.add,\n      disposeHandler: channel.sink.close,\n    );\n    final vm = await service.getVM();\n    final isolateId = vm.isolates!.where((e) => e.name == 'main').first.id!;\n    await service.streamListen(EventStreams.kExtension);\n\n    final client = ConnectClient(service, isolateId);\n    final handlers = {\n      ConnectEvent.instancesChanged.event: (_) {\n        client._instancesChangedController.add(null);\n      },\n      ConnectEvent.collectionInfoChanged.event: (Map<String, dynamic> json) {\n        final collectionInfo = ConnectCollectionInfo.fromJson(json);\n        client.collectionInfo[collectionInfo.collection] = collectionInfo;\n        client._collectionInfoChangedController.add(null);\n      },\n      ConnectEvent.queryChanged.event: (_) {\n        client._queryChangedController.add(null);\n      },\n    };\n    service.onExtensionEvent.listen((Event event) {\n      final data = event.extensionData?.data ?? {};\n      handlers[event.extensionKind]?.call(data);\n    });\n\n    return client;\n  }\n\n  Future<T> _call<T>(\n    ConnectAction action, {\n    Duration? timeout = kNormalTimeout,\n    Map<String, dynamic>? args,\n  }) async {\n    var responseFuture = vmService.callServiceExtension(\n      action.method,\n      isolateId: isolateId,\n      args: {\n        if (args != null) 'args': jsonEncode(args),\n      },\n    );\n    if (timeout != null) {\n      responseFuture = responseFuture.timeout(timeout);\n    }\n\n    final response = await responseFuture;\n    return response.json?['result'] as T;\n  }\n\n  Future<List<CollectionSchema<dynamic>>> getSchema() async {\n    final schema = await _call<List<dynamic>>(ConnectAction.getSchema);\n    return schema\n        .map(\n          (e) => CollectionSchema<dynamic>.fromJson(e as Map<String, dynamic>),\n        )\n        .toList();\n  }\n\n  Future<List<String>> listInstances() async {\n    final instances = await _call<List<dynamic>>(ConnectAction.listInstances);\n    return instances.cast();\n  }\n\n  Future<void> watchInstance(String instance) async {\n    collectionInfo.clear();\n    await _call<dynamic>(\n      ConnectAction.watchInstance,\n      args: {'instance': instance},\n    );\n  }\n\n  Future<Map<String, Object?>> executeQuery(ConnectQuery query) async {\n    return _call<Map<String, Object?>>(\n      ConnectAction.executeQuery,\n      args: query.toJson(),\n      timeout: kLongTimeout,\n    );\n  }\n\n  Future<void> removeQuery(ConnectQuery query) async {\n    await _call<dynamic>(\n      ConnectAction.removeQuery,\n      args: query.toJson(),\n      timeout: kLongTimeout,\n    );\n  }\n\n  Future<void> importJson(\n    String instance,\n    String collection,\n    List<dynamic> objects,\n  ) async {\n    await _call<dynamic>(\n      ConnectAction.importJson,\n      args: {\n        'instance': instance,\n        'collection': collection,\n        'objects': objects,\n      },\n    );\n  }\n\n  Future<List<dynamic>> exportJson(ConnectQuery query) async {\n    final data = await _call<List<dynamic>>(\n      ConnectAction.exportJson,\n      args: query.toJson(),\n      timeout: kLongTimeout,\n    );\n\n    return data.cast();\n  }\n\n  Future<void> editProperty(ConnectEdit edit) async {\n    await _call<dynamic>(\n      ConnectAction.editProperty,\n      args: edit.toJson(),\n      timeout: kLongTimeout,\n    );\n  }\n\n  Future<void> disconnect() async {\n    await vmService.dispose();\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/connected_layout.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/collection/collection_area.dart';\nimport 'package:isar_inspector/connect_client.dart';\nimport 'package:isar_inspector/sidebar.dart';\n\nclass ConnectedLayout extends StatefulWidget {\n  const ConnectedLayout({\n    super.key,\n    required this.client,\n    required this.instances,\n    required this.collections,\n  });\n\n  final ConnectClient client;\n  final List<String> instances;\n  final List<CollectionSchema<dynamic>> collections;\n\n  @override\n  State<ConnectedLayout> createState() => _ConnectedLayoutState();\n}\n\nclass _ConnectedLayoutState extends State<ConnectedLayout> {\n  late String selectedInstance;\n  late String selectedCollection = widget.collections.first.name;\n  late StreamSubscription<void> infoSubscription;\n\n  @override\n  void initState() {\n    _selectInstance(widget.instances.first);\n    infoSubscription = widget.client.collectionInfoChanged.listen((_) {\n      setState(() {});\n    });\n    super.initState();\n  }\n\n  @override\n  void didUpdateWidget(covariant ConnectedLayout oldWidget) {\n    if (!widget.instances.contains(selectedInstance)) {\n      _selectInstance(widget.instances.first);\n    }\n    super.didUpdateWidget(oldWidget);\n  }\n\n  @override\n  void dispose() {\n    infoSubscription.cancel();\n    super.dispose();\n  }\n\n  void _selectInstance(String instance) {\n    selectedInstance = instance;\n    widget.client.watchInstance(instance);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.all(25),\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        children: [\n          SizedBox(\n            width: 320,\n            child: Sidebar(\n              instances: widget.instances,\n              selectedInstance: selectedInstance,\n              onInstanceSelected: (instance) {\n                setState(() {\n                  _selectInstance(instance);\n                });\n              },\n              collections: widget.collections,\n              collectionInfo: widget.client.collectionInfo,\n              selectedCollection: selectedCollection,\n              onCollectionSelected: (collection) {\n                setState(() {\n                  selectedCollection = collection;\n                });\n              },\n            ),\n          ),\n          const SizedBox(width: 25),\n          Expanded(\n            child: CollectionArea(\n              key: Key('$selectedInstance.$selectedCollection'),\n              instance: selectedInstance,\n              collection: selectedCollection,\n              client: widget.client,\n              schemas: {\n                for (final schema in widget.collections) ...{\n                  schema.name: schema,\n                  for (final embedded in schema.embeddedSchemas.values) ...{\n                    embedded.name: embedded,\n                  }\n                }\n              },\n            ),\n          )\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/connection_screen.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/connect_client.dart';\nimport 'package:isar_inspector/connected_layout.dart';\nimport 'package:isar_inspector/error_screen.dart';\n\nclass ConnectionScreen extends StatefulWidget {\n  const ConnectionScreen({\n    super.key,\n    required this.port,\n    required this.secret,\n  });\n\n  final String port;\n  final String secret;\n\n  @override\n  State<ConnectionScreen> createState() => _ConnectionPageState();\n}\n\nclass _ConnectionPageState extends State<ConnectionScreen> {\n  late Future<ConnectClient> clientFuture;\n\n  @override\n  void initState() {\n    clientFuture = ConnectClient.connect(widget.port, widget.secret);\n    super.initState();\n  }\n\n  @override\n  void didUpdateWidget(covariant ConnectionScreen oldWidget) {\n    if (oldWidget.port != widget.port || oldWidget.secret != widget.secret) {\n      clientFuture = ConnectClient.connect(widget.port, widget.secret);\n    }\n    super.didUpdateWidget(oldWidget);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return FutureBuilder<ConnectClient>(\n      future: clientFuture,\n      builder: (context, snapshot) {\n        if (snapshot.hasData) {\n          return _SchemaLoader(client: snapshot.data!);\n        } else if (snapshot.hasError) {\n          return const ErrorScreen();\n        } else {\n          return const Loading();\n        }\n      },\n    );\n  }\n}\n\nclass _SchemaLoader extends StatefulWidget {\n  const _SchemaLoader({required this.client});\n\n  final ConnectClient client;\n\n  @override\n  State<_SchemaLoader> createState() => _SchemaLoaderState();\n}\n\nclass _SchemaLoaderState extends State<_SchemaLoader> {\n  late Future<List<String>> instancesFuture;\n  late Future<List<CollectionSchema<dynamic>>> collectionsFuture;\n  late StreamSubscription<void> _instancesSubscription;\n\n  @override\n  void initState() {\n    instancesFuture = widget.client.listInstances();\n    collectionsFuture = widget.client.getSchema();\n    _instancesSubscription = widget.client.instancesChanged.listen((event) {\n      setState(() {\n        instancesFuture = widget.client.listInstances();\n      });\n    });\n    super.initState();\n  }\n\n  @override\n  void didUpdateWidget(covariant _SchemaLoader oldWidget) {\n    instancesFuture = widget.client.listInstances();\n    collectionsFuture = widget.client.getSchema();\n    super.didUpdateWidget(oldWidget);\n  }\n\n  @override\n  void dispose() {\n    _instancesSubscription.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return FutureBuilder<List<dynamic>>(\n      future: Future.wait([instancesFuture, collectionsFuture]),\n      builder: (context, snapshot) {\n        if (snapshot.hasData) {\n          return ConnectedLayout(\n            client: widget.client,\n            instances: snapshot.data![0] as List<String>,\n            collections: snapshot.data![1] as List<CollectionSchema<dynamic>>,\n          );\n        } else if (snapshot.hasError) {\n          return const ErrorScreen();\n        } else {\n          return const Loading();\n        }\n      },\n    );\n  }\n}\n\nclass Loading extends StatelessWidget {\n  const Loading({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return const Center(\n      child: CircularProgressIndicator(),\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/error_screen.dart",
    "content": "// ignore: avoid_web_libraries_in_flutter\nimport 'dart:html';\n\nimport 'package:flutter/material.dart';\n\nclass ErrorScreen extends StatelessWidget {\n  const ErrorScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          const Text(\n            'Disconnected',\n            style: TextStyle(fontSize: 20),\n          ),\n          const SizedBox(height: 10),\n          const Text('Please make sure your Isar instance is running.'),\n          const SizedBox(height: 40),\n          ElevatedButton(\n            onPressed: window.location.reload,\n            child: const Text('Retry Connection'),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/instance_selector.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:font_awesome_flutter/font_awesome_flutter.dart';\n\nclass InstanceSelector extends StatefulWidget {\n  const InstanceSelector({\n    super.key,\n    required this.instances,\n    required this.selectedInstance,\n    required this.onSelected,\n  });\n\n  final List<String> instances;\n  final String selectedInstance;\n  final void Function(String instance) onSelected;\n\n  @override\n  State<InstanceSelector> createState() => _InstanceSelectorState();\n}\n\nclass _InstanceSelectorState extends State<InstanceSelector>\n    with SingleTickerProviderStateMixin {\n  late final AnimationController _controller = AnimationController(\n    duration: const Duration(milliseconds: 500),\n    vsync: this,\n  );\n\n  late final Animation<double> _animation = CurvedAnimation(\n    parent: _controller,\n    curve: Curves.easeOut,\n  );\n\n  @override\n  void initState() {\n    _animation.addStatusListener((AnimationStatus status) {\n      setState(() {});\n    });\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.stretch,\n      children: [\n        Stack(\n          alignment: Alignment.bottomCenter,\n          children: [\n            Card(\n              margin: const EdgeInsets.all(10),\n              color: theme.colorScheme.secondaryContainer,\n              child: SizeTransition(\n                sizeFactor: _animation,\n                axisAlignment: -1,\n                child: Column(\n                  mainAxisSize: MainAxisSize.min,\n                  crossAxisAlignment: CrossAxisAlignment.stretch,\n                  children: [\n                    const SizedBox(height: 10),\n                    for (var instance in widget.instances)\n                      if (instance != widget.selectedInstance)\n                        InstanceButton(\n                          instance: instance,\n                          onTap: () {\n                            widget.onSelected(instance);\n                            _controller.reverse();\n                          },\n                        ),\n                    const SizedBox(height: 75),\n                  ],\n                ),\n              ),\n            ),\n            SelectedInstanceButton(\n              instance: widget.selectedInstance,\n              hasMultiple: widget.instances.length > 1,\n              color: _animation.status != AnimationStatus.dismissed\n                  ? Colors.blue\n                  : null,\n              onTap: () {\n                if (_controller.status == AnimationStatus.completed) {\n                  _controller.reverse();\n                } else {\n                  _controller.forward();\n                }\n              },\n            ),\n          ],\n        ),\n      ],\n    );\n  }\n}\n\nclass InstanceButton extends StatelessWidget {\n  const InstanceButton({\n    super.key,\n    required this.instance,\n    required this.onTap,\n  });\n\n  final String instance;\n  final VoidCallback onTap;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 10),\n      child: Card(\n        margin: EdgeInsets.zero,\n        color: Colors.transparent,\n        clipBehavior: Clip.antiAlias,\n        child: InkWell(\n          onTap: onTap,\n          child: Padding(\n            padding: const EdgeInsets.all(20),\n            child: Center(\n              child: Text(\n                instance,\n                textAlign: TextAlign.start,\n                style: const TextStyle(\n                  color: Colors.white,\n                  fontSize: 16,\n                  fontWeight: FontWeight.bold,\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass SelectedInstanceButton extends StatelessWidget {\n  const SelectedInstanceButton({\n    super.key,\n    required this.instance,\n    required this.onTap,\n    required this.hasMultiple,\n    required this.color,\n  });\n\n  final String instance;\n  final VoidCallback onTap;\n  final bool hasMultiple;\n  final Color? color;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Card(\n      margin: const EdgeInsets.all(10),\n      color: theme.colorScheme.secondaryContainer,\n      clipBehavior: Clip.antiAlias,\n      child: InkWell(\n        onTap: hasMultiple ? onTap : null,\n        child: Center(\n          child: Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),\n            child: Row(\n              children: [\n                Icon(\n                  FontAwesomeIcons.database,\n                  size: 25,\n                  color: theme.colorScheme.onSecondaryContainer,\n                ),\n                const SizedBox(width: 10),\n                Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: [\n                    Text(\n                      instance,\n                      style: TextStyle(\n                        fontWeight: FontWeight.bold,\n                        fontSize: 16,\n                        color: theme.colorScheme.onSecondaryContainer,\n                      ),\n                    ),\n                    Text(\n                      'Isar Instance',\n                      style: theme.textTheme.bodyMedium!.copyWith(\n                        color: theme.colorScheme.onSecondaryContainer,\n                      ),\n                    )\n                  ],\n                ),\n                const Spacer(),\n                if (hasMultiple)\n                  Column(\n                    mainAxisSize: MainAxisSize.min,\n                    children: const [\n                      Icon(\n                        FontAwesomeIcons.chevronUp,\n                        size: 12,\n                      ),\n                      Icon(\n                        FontAwesomeIcons.chevronDown,\n                        size: 12,\n                      ),\n                    ],\n                  ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/main.dart",
    "content": "// ignore: avoid_web_libraries_in_flutter\nimport 'dart:html';\n\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\n\nimport 'package:isar_inspector/connection_screen.dart';\n\nvoid main() async {\n  if (window.navigator.userAgent.toLowerCase().contains('chrome')) {\n    runApp(\n      DarkMode(\n        notifier: DarkModeNotifier(),\n        child: const App(),\n      ),\n    );\n  } else {\n    runApp(const UnsupportedBrowser());\n  }\n}\n\nclass UnsupportedBrowser extends StatelessWidget {\n  const UnsupportedBrowser({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Isar Inspector',\n      theme: ThemeData.from(\n        colorScheme: ColorScheme.fromSeed(\n          seedColor: const Color(0xFF9FC9FF),\n          brightness: Brightness.dark,\n        ),\n        useMaterial3: true,\n      ),\n      home: const Scaffold(\n        body: Center(\n          child: Text(\n            'This browser is not supported. Please use a Chrome based browser.',\n            textAlign: TextAlign.center,\n            style: TextStyle(fontSize: 18),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nfinal _router = GoRouter(\n  routes: <GoRoute>[\n    GoRoute(\n      path: '/',\n      builder: (BuildContext context, GoRouterState state) {\n        return const Material(\n          child: Center(\n            child: Text(\n              'Welcome to the Isar Inspector!\\nPlease open the link '\n              'displayed when running the debug version of an Isar app.',\n              textAlign: TextAlign.center,\n              style: TextStyle(fontSize: 18),\n            ),\n          ),\n        );\n      },\n    ),\n    GoRoute(\n      path: '/:port/:secret',\n      builder: (BuildContext context, GoRouterState state) {\n        return GestureDetector(\n          onTap: () {\n            FocusScope.of(context).requestFocus(FocusNode());\n          },\n          child: Scaffold(\n            body: Material(\n              child: ConnectionScreen(\n                port: state.params['port']!,\n                secret: state.params['secret']!,\n              ),\n            ),\n          ),\n        );\n      },\n    ),\n  ],\n);\n\nclass App extends StatelessWidget {\n  const App({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp.router(\n      title: 'Isar Inspector',\n      routeInformationProvider: _router.routeInformationProvider,\n      routeInformationParser: _router.routeInformationParser,\n      routerDelegate: _router.routerDelegate,\n      theme: ThemeData.from(\n        colorScheme: ColorScheme.fromSeed(\n          seedColor: const Color(0xFF9FC9FF),\n          brightness: DarkMode.of(context).darkMode\n              ? Brightness.dark\n              : Brightness.light,\n        ),\n        useMaterial3: true,\n      ),\n    );\n  }\n}\n\nclass DarkMode extends InheritedNotifier<DarkModeNotifier> {\n  const DarkMode({\n    super.key,\n    super.notifier,\n    required super.child,\n  });\n\n  static DarkModeNotifier of(BuildContext context) {\n    return context.dependOnInheritedWidgetOfExactType<DarkMode>()!.notifier!;\n  }\n}\n\nclass DarkModeNotifier extends ChangeNotifier {\n  var _darkMode = true;\n\n  bool get darkMode => _darkMode;\n\n  void toggle() {\n    _darkMode = !_darkMode;\n    notifyListeners();\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/isar_object.dart",
    "content": "class IsarObject {\n  const IsarObject(\n    this.data,\n  );\n\n  final Map<String, dynamic> data;\n\n  dynamic getValue(String propertyName) => data[propertyName];\n\n  IsarObject? getNested(String propertyName, {String? linkCollection}) {\n    final data = this.data[propertyName] as Map<String, dynamic>?;\n    if (data != null) {\n      return IsarObject(data);\n    } else {\n      return null;\n    }\n  }\n\n  List<IsarObject?>? getNestedList(\n    String propertyName, {\n    String? linkCollection,\n  }) {\n    final list = data[propertyName] as List<dynamic>?;\n    if (list == null) {\n      return null;\n    }\n\n    final objects = <IsarObject?>[];\n    for (var i = 0; i < list.length; i++) {\n      final data = list[i] as Map<String, dynamic>;\n      objects.add(IsarObject(data));\n    }\n\n    return objects;\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/object_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/object/isar_object.dart';\nimport 'package:isar_inspector/object/property_embedded_view.dart';\nimport 'package:isar_inspector/object/property_link_view.dart';\nimport 'package:isar_inspector/object/property_view.dart';\n\nclass ObjectView extends StatelessWidget {\n  const ObjectView({\n    super.key,\n    this.root = false,\n    required this.schemaName,\n    required this.schemas,\n    required this.object,\n    required this.onUpdate,\n  });\n\n  final bool root;\n  final String schemaName;\n  final Map<String, Schema<dynamic>> schemas;\n  final IsarObject object;\n  final void Function(\n    String collection,\n    int? id,\n    String path,\n    dynamic value,\n  ) onUpdate;\n\n  @override\n  Widget build(BuildContext context) {\n    final schema = schemas[schemaName]!;\n    final id = schema is CollectionSchema\n        ? object.getValue(schema.idName) as int\n        : null;\n    return Column(\n      children: [\n        if (schema is CollectionSchema)\n          PropertyView(\n            property: PropertySchema(\n              id: -1,\n              name: schema.idName,\n              type: IsarType.long,\n            ),\n            value: id,\n            isId: true,\n            isIndexed: false,\n            onUpdate: (_) => throw UnimplementedError(),\n          ),\n        for (final property in schema.properties.values)\n          if (property.target == null)\n            PropertyView(\n              property: property,\n              value: object.getValue(property.name),\n              isId: false,\n              isIndexed: schema is CollectionSchema &&\n                  schema.indexes.values.any(\n                    (index) => index.properties.any(\n                      (p) => p.name == property.name,\n                    ),\n                  ),\n              onUpdate: (value) {\n                onUpdate(schemaName, id, property.name, value);\n              },\n            )\n          else\n            EmbeddedPropertyView(\n              property: property,\n              schemas: schemas,\n              object: object,\n              onUpdate: (_, path, value) {\n                onUpdate(schemaName, id, '${property.name}.$path', value);\n              },\n            ),\n        if (root && schema is CollectionSchema)\n          for (final link in schema.links.values)\n            LinkPropertyView(\n              link: link,\n              schemas: schemas,\n              object: object,\n              onUpdate: (id, path, value) {\n                onUpdate(link.target, id, path, value);\n              },\n            ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/property_builder.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:google_fonts/google_fonts.dart';\n\nclass PropertyBuilder extends StatefulWidget {\n  const PropertyBuilder({\n    super.key,\n    required this.property,\n    this.underline = false,\n    this.value,\n    required this.type,\n    this.children = const [],\n  });\n\n  final String property;\n  final bool underline;\n  final Widget? value;\n  final String type;\n  final List<Widget> children;\n\n  @override\n  State<PropertyBuilder> createState() => _PropertyBuilderState();\n}\n\nclass _PropertyBuilderState extends State<PropertyBuilder> {\n  var _expanded = false;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      crossAxisAlignment: CrossAxisAlignment.stretch,\n      children: [\n        InkWell(\n          onTap: widget.children.isNotEmpty\n              ? () => setState(() => _expanded = !_expanded)\n              : null,\n          customBorder: RoundedRectangleBorder(\n            borderRadius: BorderRadius.circular(20),\n          ),\n          child: Padding(\n            padding: const EdgeInsets.symmetric(\n              horizontal: 10,\n              vertical: 5,\n            ),\n            child: Row(\n              children: [\n                if (widget.children.isNotEmpty) ...[\n                  AnimatedRotation(\n                    turns: _expanded ? 0.25 : 0,\n                    duration: const Duration(milliseconds: 200),\n                    child: Icon(\n                      Icons.arrow_right,\n                      size: 24,\n                      color: theme.colorScheme.onPrimaryContainer,\n                    ),\n                  ),\n                  const SizedBox(width: 4),\n                ] else\n                  const SizedBox(width: 28),\n                Tooltip(\n                  message: widget.type,\n                  child: Text(\n                    '${widget.property}:',\n                    style: GoogleFonts.jetBrainsMono(\n                      fontWeight: FontWeight.w500,\n                      color: theme.colorScheme.onPrimaryContainer,\n                      decoration:\n                          widget.underline ? TextDecoration.underline : null,\n                    ),\n                  ),\n                ),\n                const SizedBox(width: 8),\n                if (widget.value != null)\n                  Expanded(child: widget.value!)\n                else\n                  Text(\n                    widget.type,\n                    style: TextStyle(\n                      color:\n                          theme.colorScheme.onPrimaryContainer.withOpacity(0.5),\n                    ),\n                  ),\n              ],\n            ),\n          ),\n        ),\n        if (_expanded && widget.children.isNotEmpty)\n          Padding(\n            padding: const EdgeInsets.only(left: 20),\n            child: AnimatedContainer(\n              duration: const Duration(milliseconds: 200),\n              child: Column(children: widget.children),\n            ),\n          ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/property_embedded_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/object/isar_object.dart';\nimport 'package:isar_inspector/object/object_view.dart';\nimport 'package:isar_inspector/object/property_builder.dart';\nimport 'package:isar_inspector/object/property_value.dart';\n\nclass EmbeddedPropertyView extends StatelessWidget {\n  const EmbeddedPropertyView({\n    super.key,\n    required this.property,\n    required this.schemas,\n    required this.object,\n    required this.onUpdate,\n  });\n\n  final PropertySchema property;\n  final Map<String, Schema<dynamic>> schemas;\n  final IsarObject object;\n  final void Function(int? id, String path, dynamic value) onUpdate;\n\n  @override\n  Widget build(BuildContext context) {\n    if (property.type == IsarType.object) {\n      final child = object.getNested(property.name);\n      return PropertyBuilder(\n        property: property.name,\n        type: property.target!,\n        value: child == null ? const NullValue() : null,\n        children: [\n          if (child != null)\n            ObjectView(\n              schemaName: property.target!,\n              schemas: schemas,\n              object: child,\n              onUpdate: (_, id, path, value) {\n                onUpdate(id, path, value);\n              },\n            ),\n        ],\n      );\n    } else {\n      final children = object.getNestedList(property.name);\n      final childrenLength = children != null ? '(${children.length})' : '';\n      return PropertyBuilder(\n        property: property.name,\n        type: 'List<${property.target}> $childrenLength',\n        value: children == null ? const NullValue() : null,\n        children: [\n          for (var i = 0; i < (children?.length ?? 0); i++)\n            PropertyBuilder(\n              property: '$i',\n              type: property.target!,\n              value: children![i] == null ? const NullValue() : null,\n              children: [\n                if (children[i] != null)\n                  ObjectView(\n                    schemaName: property.target!,\n                    schemas: schemas,\n                    object: children[i]!,\n                    onUpdate: (_, id, path, value) {\n                      onUpdate(id, '$i.$path', value);\n                    },\n                  ),\n              ],\n            ),\n        ],\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/property_link_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/object/isar_object.dart';\nimport 'package:isar_inspector/object/object_view.dart';\nimport 'package:isar_inspector/object/property_builder.dart';\nimport 'package:isar_inspector/object/property_value.dart';\n\nclass LinkPropertyView extends StatelessWidget {\n  const LinkPropertyView({\n    super.key,\n    required this.link,\n    required this.schemas,\n    required this.object,\n    required this.onUpdate,\n  });\n\n  final LinkSchema link;\n  final Map<String, Schema<dynamic>> schemas;\n  final IsarObject object;\n  final void Function(int id, String path, dynamic value) onUpdate;\n\n  @override\n  Widget build(BuildContext context) {\n    if (link.single) {\n      final child = object.getNested(\n        link.name,\n        linkCollection: link.target,\n      );\n      return PropertyBuilder(\n        property: link.name,\n        type: 'IsarLink<${link.target}>',\n        value: child == null ? const NullValue() : null,\n        children: [\n          if (child != null)\n            ObjectView(\n              schemaName: link.target,\n              schemas: schemas,\n              object: child,\n              onUpdate: (_, id, path, value) {\n                onUpdate(id!, path, value);\n              },\n            ),\n        ],\n      );\n    } else {\n      final children = object.getNestedList(\n        link.name,\n        linkCollection: link.target,\n      );\n      final childrenLength = children != null ? '(${children.length})' : '';\n      return PropertyBuilder(\n        property: link.name,\n        type: 'IsarLinks<${link.target}> $childrenLength',\n        value: children == null ? const NullValue() : null,\n        children: [\n          for (var i = 0; i < (children?.length ?? 0); i++)\n            PropertyBuilder(\n              property: '$i',\n              type: link.target,\n              value: children![i] == null ? const NullValue() : null,\n              children: [\n                if (children[i] != null)\n                  ObjectView(\n                    schemaName: link.target,\n                    schemas: schemas,\n                    object: children[i]!,\n                    onUpdate: (_, id, path, value) {\n                      onUpdate(id!, path, value);\n                    },\n                  ),\n              ],\n            ),\n        ],\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/property_value.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:google_fonts/google_fonts.dart';\nimport 'package:isar/isar.dart';\n\nclass PropertyValue extends StatelessWidget {\n  const PropertyValue(\n    this.value, {\n    super.key,\n    required this.enumMap,\n    required this.type,\n    this.onUpdate,\n  });\n\n  final dynamic value;\n  final IsarType type;\n  final Map<String, dynamic>? enumMap;\n  final void Function(dynamic newValue)? onUpdate;\n\n  @override\n  Widget build(BuildContext context) {\n    final value = this.value;\n\n    if (enumMap != null) {\n      final enumName = enumMap!.entries.firstWhere(\n        (e) => e.value == value,\n        orElse: () {\n          if (type == IsarType.byte || type == IsarType.byteList) {\n            return enumMap!.entries.first;\n          } else {\n            return const MapEntry('null', null);\n          }\n        },\n      ).key;\n      return GestureDetector(\n        onTapDown: onUpdate == null\n            ? null\n            : (TapDownDetails details) async {\n                final newValue = await showMenu(\n                  context: context,\n                  position: RelativeRect.fromLTRB(\n                    details.globalPosition.dx,\n                    details.globalPosition.dy,\n                    100000,\n                    0,\n                  ),\n                  items: [\n                    if (type != IsarType.byte && type != IsarType.byteList)\n                      const PopupMenuItem<dynamic>(\n                        child: Text('null'),\n                      ),\n                    for (final enumName in enumMap!.keys)\n                      PopupMenuItem(\n                        value: enumMap![enumName],\n                        child: Text(enumName),\n                      ),\n                  ],\n                );\n                onUpdate?.call(newValue);\n              },\n        child: Text(\n          enumName,\n          style: GoogleFonts.jetBrainsMono(\n            color: enumName != 'null' ? Colors.yellow : Colors.grey,\n            fontWeight: FontWeight.bold,\n          ),\n        ),\n      );\n    }\n\n    switch (type) {\n      case IsarType.bool:\n      case IsarType.boolList:\n        return GestureDetector(\n          onTapDown: onUpdate == null\n              ? null\n              : (TapDownDetails details) async {\n                  final newValue = await showMenu(\n                    context: context,\n                    position: RelativeRect.fromLTRB(\n                      details.globalPosition.dx,\n                      details.globalPosition.dy,\n                      100000,\n                      0,\n                    ),\n                    items: const [\n                      PopupMenuItem<bool?>(\n                        child: Text('null'),\n                      ),\n                      PopupMenuItem(\n                        value: true,\n                        child: Text('true'),\n                      ),\n                      PopupMenuItem(\n                        value: false,\n                        child: Text('false'),\n                      ),\n                    ],\n                  );\n                  onUpdate?.call(newValue);\n                },\n          child: Text(\n            '$value',\n            style: GoogleFonts.jetBrainsMono(\n              color: value != null ? Colors.orange : Colors.grey,\n              fontWeight: FontWeight.bold,\n            ),\n          ),\n        );\n      case IsarType.byte:\n      case IsarType.byteList:\n      case IsarType.int:\n      case IsarType.intList:\n      case IsarType.float:\n      case IsarType.floatList:\n      case IsarType.long:\n      case IsarType.longList:\n      case IsarType.double:\n      case IsarType.doubleList:\n        final numController = TextEditingController(\n          text: value == null\n              ? null\n              : value == null\n                  ? null\n                  : '$value',\n        );\n        final numFocus = FocusNode();\n        numFocus.addListener(() {\n          if (!numFocus.hasPrimaryFocus) {\n            final value = numController.text;\n            num? numOrNull;\n            if (type == IsarType.float ||\n                type == IsarType.floatList ||\n                type == IsarType.double ||\n                type == IsarType.doubleList) {\n              numOrNull = double.tryParse(value);\n            } else {\n              numOrNull = int.tryParse(value);\n            }\n            onUpdate?.call(numOrNull);\n          }\n        });\n        return TextField(\n          controller: numController,\n          focusNode: numFocus,\n          enabled: onUpdate != null,\n          decoration: InputDecoration.collapsed(\n            hintText: 'null',\n            hintStyle: GoogleFonts.jetBrainsMono(\n              color: Colors.grey,\n              fontWeight: FontWeight.bold,\n              fontSize: 14,\n            ),\n          ),\n          style: GoogleFonts.jetBrainsMono(\n            color: Colors.blue,\n            fontWeight: FontWeight.bold,\n            fontSize: 14,\n          ),\n        );\n      case IsarType.dateTime:\n      case IsarType.dateTimeList:\n        final date = value != null\n            ? DateTime.fromMicrosecondsSinceEpoch(value as int)\n            : null;\n        return GestureDetector(\n          onTap: onUpdate == null\n              ? null\n              : () async {\n                  final newDate = await showDatePicker(\n                    context: context,\n                    initialDate: date ?? DateTime.now(),\n                    firstDate: DateTime(1970),\n                    lastDate: DateTime(2050),\n                  );\n                  onUpdate?.call(newDate?.microsecondsSinceEpoch);\n                },\n          child: Text(\n            date?.toIso8601String() ?? 'null',\n            style: GoogleFonts.jetBrainsMono(\n              color: date != null ? Colors.blue : Colors.grey,\n              fontWeight: FontWeight.bold,\n            ),\n          ),\n        );\n      case IsarType.string:\n      case IsarType.stringList:\n        final strController = TextEditingController(\n          text: value == null\n              ? null\n              : '\"${value.toString().replaceAll('\\n', '⤵')}\"',\n        );\n        final strFocus = FocusNode();\n        strFocus.addListener(() {\n          if (!strFocus.hasPrimaryFocus) {\n            final value = strController.text;\n            String? strOrNull;\n            if (value.startsWith('\"') && value.endsWith('\"')) {\n              strOrNull =\n                  value.substring(1, value.length - 1).replaceAll('⤵', '\\n');\n            }\n            onUpdate?.call(strOrNull);\n          }\n        });\n        return TextField(\n          controller: strController,\n          focusNode: strFocus,\n          enabled: onUpdate != null,\n          decoration: InputDecoration.collapsed(\n            hintText: 'null',\n            hintStyle: GoogleFonts.jetBrainsMono(\n              color: Colors.grey,\n              fontWeight: FontWeight.bold,\n              fontSize: 14,\n            ),\n          ),\n          style: GoogleFonts.jetBrainsMono(\n            color: Colors.green,\n            fontWeight: FontWeight.bold,\n            fontSize: 14,\n          ),\n        );\n      case IsarType.object:\n      case IsarType.objectList:\n        throw ArgumentError('Invalid type');\n    }\n  }\n}\n\nclass NullValue extends StatelessWidget {\n  const NullValue({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Text(\n      'null',\n      style: GoogleFonts.jetBrainsMono(\n        color: Colors.grey,\n        fontWeight: FontWeight.bold,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/object/property_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/object/property_builder.dart';\nimport 'package:isar_inspector/object/property_value.dart';\n\nclass PropertyView extends StatelessWidget {\n  const PropertyView({\n    super.key,\n    required this.property,\n    required this.value,\n    required this.isId,\n    required this.isIndexed,\n    required this.onUpdate,\n  });\n\n  final PropertySchema property;\n  final dynamic value;\n  final bool isId;\n  final bool isIndexed;\n  final void Function(dynamic value) onUpdate;\n\n  @override\n  Widget build(BuildContext context) {\n    final value = this.value;\n    final valueLength =\n        // ignore: avoid_dynamic_calls\n        value is String || value is List ? '(${value.length})' : '';\n    return PropertyBuilder(\n      property: property.name,\n      underline: isIndexed,\n      type: isId ? 'Id' : '${property.type.typeName} $valueLength',\n      value: value is List\n          ? null\n          : property.type.isList\n              ? const NullValue()\n              : PropertyValue(\n                  value,\n                  type: property.type,\n                  enumMap: property.enumMap,\n                  onUpdate: isId ? null : onUpdate,\n                ),\n      children: [\n        if (value is List)\n          for (var i = 0; i < value.length; i++)\n            PropertyBuilder(\n              property: '$i',\n              type: property.type.typeName,\n              value: PropertyValue(\n                value[i],\n                type: property.type,\n                enumMap: property.enumMap,\n                onUpdate: onUpdate,\n              ),\n            ),\n      ],\n    );\n  }\n}\n\nextension TypeName on IsarType {\n  String get typeName {\n    switch (this) {\n      case IsarType.bool:\n        return 'bool';\n      case IsarType.byte:\n        return 'byte';\n      case IsarType.int:\n        return 'short';\n      case IsarType.long:\n        return 'int';\n      case IsarType.float:\n        return 'float';\n      case IsarType.double:\n        return 'double';\n      case IsarType.dateTime:\n        return 'DateTime';\n      case IsarType.string:\n        return 'String';\n      case IsarType.object:\n        return 'Object';\n      case IsarType.boolList:\n        return 'List<bool>';\n      case IsarType.byteList:\n        return 'List<byte>';\n      case IsarType.intList:\n        return 'List<short>';\n      case IsarType.longList:\n        return 'List<int>';\n      case IsarType.floatList:\n        return 'List<float>';\n      case IsarType.doubleList:\n        return 'List<double>';\n      case IsarType.dateTimeList:\n        return 'List<DateTime>';\n      case IsarType.stringList:\n        return 'List<String>';\n      case IsarType.objectList:\n        return 'List<Object>';\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/query_builder/query_filter.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/object/property_value.dart';\nimport 'package:isar_inspector/util.dart';\n\nclass QueryFilter extends StatelessWidget {\n  const QueryFilter({\n    super.key,\n    required this.collection,\n    required this.condition,\n    required this.onChanged,\n  });\n\n  final CollectionSchema<dynamic> collection;\n  final FilterCondition condition;\n  final void Function(FilterCondition filter) onChanged;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final property = collection.propertyOrId(condition.property);\n    return Container(\n      height: 60,\n      decoration: BoxDecoration(\n        border: Border.all(color: theme.colorScheme.outline.withOpacity(0.5)),\n        borderRadius: BorderRadius.circular(16),\n      ),\n      child: Padding(\n        padding: const EdgeInsets.all(15),\n        child: Row(\n          children: [\n            DropdownButtonHideUnderline(\n              child: DropdownButton<String>(\n                isDense: true,\n                items: [\n                  for (final property in collection.idAndProperties)\n                    if (property.type != IsarType.object &&\n                        property.type != IsarType.objectList)\n                      DropdownMenuItem(\n                        value: property.name,\n                        child: Text(property.name),\n                      ),\n                ],\n                value: condition.property,\n                onChanged: (value) {\n                  if (value == null) return;\n                  final newProperty = collection.propertyOrId(value);\n                  onChanged(\n                    FilterCondition(\n                      type: FilterConditionType.equalTo,\n                      property: value,\n                      value1: newProperty.defaultEditingValue,\n                      value2: newProperty.defaultEditingValue,\n                      include1: false,\n                      include2: false,\n                      caseSensitive: false,\n                    ),\n                  );\n                },\n              ),\n            ),\n            const SizedBox(width: 20),\n            DropdownButtonHideUnderline(\n              child: DropdownButton<FilterConditionType>(\n                isDense: true,\n                items: [\n                  for (final type in property.supportedFilters)\n                    DropdownMenuItem(\n                      value: type,\n                      child: Text(type.niceName),\n                    ),\n                ],\n                value: condition.type,\n                onChanged: (value) {\n                  if (value == null) return;\n                  onChanged(\n                    FilterCondition(\n                      type: value,\n                      property: condition.property,\n                      value1: condition.value1,\n                      include1: value == FilterConditionType.between,\n                      value2: condition.value2,\n                      include2: value == FilterConditionType.between,\n                      caseSensitive: false,\n                    ),\n                  );\n                },\n              ),\n            ),\n            if (condition.type.valueCount > 0) ...[\n              const SizedBox(width: 20),\n              SizedBox(\n                width: 100,\n                child: PropertyValue(\n                  condition.value1,\n                  type: property.type,\n                  enumMap: property.enumMap,\n                  onUpdate: (newValue) {\n                    onChanged(\n                      FilterCondition(\n                        type: condition.type,\n                        property: condition.property,\n                        value1: newValue,\n                        include1: condition.include1,\n                        value2: condition.value2,\n                        include2: condition.include2,\n                        caseSensitive: false,\n                      ),\n                    );\n                  },\n                ),\n              ),\n            ],\n            if (condition.type.valueCount == 2) ...[\n              const SizedBox(width: 20),\n              SizedBox(\n                width: 100,\n                child: PropertyValue(\n                  condition.value2,\n                  type: property.type,\n                  enumMap: property.enumMap,\n                  onUpdate: (newValue) {\n                    onChanged(\n                      FilterCondition(\n                        type: condition.type,\n                        property: condition.property,\n                        value1: condition.value1,\n                        include1: condition.include1,\n                        value2: newValue,\n                        include2: condition.include2,\n                        caseSensitive: false,\n                      ),\n                    );\n                  },\n                ),\n              ),\n            ],\n          ],\n        ),\n      ),\n    );\n  }\n\n  dynamic get value1 {}\n}\n\nextension on PropertySchema {\n  List<FilterConditionType> get supportedFilters {\n    switch (type) {\n      case IsarType.bool:\n      case IsarType.boolList:\n        return [\n          FilterConditionType.equalTo,\n          FilterConditionType.isNull,\n          FilterConditionType.isNotNull,\n          if (type == IsarType.boolList) ...[\n            FilterConditionType.elementIsNull,\n            FilterConditionType.elementIsNotNull,\n            FilterConditionType.listLength,\n          ],\n        ];\n      case IsarType.byte:\n      case IsarType.byteList:\n        return [\n          FilterConditionType.equalTo,\n          FilterConditionType.greaterThan,\n          FilterConditionType.lessThan,\n          FilterConditionType.between,\n          if (type == IsarType.byteList) FilterConditionType.listLength,\n        ];\n      case IsarType.int:\n      case IsarType.float:\n      case IsarType.long:\n      case IsarType.double:\n      case IsarType.dateTime:\n      case IsarType.intList:\n      case IsarType.floatList:\n      case IsarType.longList:\n      case IsarType.doubleList:\n      case IsarType.dateTimeList:\n        return [\n          FilterConditionType.equalTo,\n          FilterConditionType.greaterThan,\n          FilterConditionType.lessThan,\n          FilterConditionType.between,\n          FilterConditionType.isNull,\n          FilterConditionType.isNotNull,\n          FilterConditionType.elementIsNull,\n          FilterConditionType.elementIsNotNull,\n          FilterConditionType.listLength,\n        ];\n      case IsarType.string:\n      case IsarType.stringList:\n        return [\n          FilterConditionType.equalTo,\n          FilterConditionType.greaterThan,\n          FilterConditionType.lessThan,\n          FilterConditionType.between,\n          FilterConditionType.startsWith,\n          FilterConditionType.endsWith,\n          FilterConditionType.contains,\n          FilterConditionType.matches,\n          FilterConditionType.isNull,\n          FilterConditionType.isNotNull,\n          if (type == IsarType.stringList) ...[\n            FilterConditionType.elementIsNull,\n            FilterConditionType.elementIsNotNull,\n            FilterConditionType.listLength,\n          ],\n        ];\n      case IsarType.object:\n      case IsarType.objectList:\n        return [];\n    }\n  }\n\n  dynamic get defaultEditingValue {\n    if (enumMap != null) {\n      return enumMap!.values.first;\n    }\n    switch (type) {\n      case IsarType.bool:\n      case IsarType.boolList:\n        return false;\n      case IsarType.byte:\n      case IsarType.byteList:\n      case IsarType.int:\n      case IsarType.intList:\n      case IsarType.long:\n      case IsarType.longList:\n        return 0;\n      case IsarType.float:\n      case IsarType.floatList:\n      case IsarType.double:\n      case IsarType.doubleList:\n        return 0.0;\n      case IsarType.dateTime:\n      case IsarType.dateTimeList:\n        return DateTime.now().microsecondsSinceEpoch;\n      case IsarType.string:\n      case IsarType.stringList:\n        return '';\n      case IsarType.object:\n      case IsarType.objectList:\n        return null;\n    }\n  }\n}\n\nextension on FilterConditionType {\n  String get niceName {\n    switch (this) {\n      case FilterConditionType.equalTo:\n        return 'is equal to';\n      case FilterConditionType.greaterThan:\n        return 'is greater than';\n      case FilterConditionType.lessThan:\n        return 'is less than';\n      case FilterConditionType.between:\n        return 'is between';\n      case FilterConditionType.startsWith:\n        return 'starts with';\n      case FilterConditionType.endsWith:\n        return 'ends with';\n      case FilterConditionType.contains:\n        return 'contains';\n      case FilterConditionType.matches:\n        return 'matches';\n      case FilterConditionType.isNull:\n        return 'is null';\n      case FilterConditionType.isNotNull:\n        return 'is not null';\n      case FilterConditionType.elementIsNull:\n        return 'element is null';\n      case FilterConditionType.elementIsNotNull:\n        return 'element is not null';\n      case FilterConditionType.listLength:\n        return 'list length between';\n    }\n  }\n\n  int get valueCount {\n    switch (this) {\n      case FilterConditionType.isNull:\n      case FilterConditionType.isNotNull:\n      case FilterConditionType.elementIsNull:\n      case FilterConditionType.elementIsNotNull:\n        return 0;\n      case FilterConditionType.equalTo:\n      case FilterConditionType.greaterThan:\n      case FilterConditionType.lessThan:\n      case FilterConditionType.startsWith:\n      case FilterConditionType.endsWith:\n      case FilterConditionType.contains:\n      case FilterConditionType.matches:\n        return 1;\n      case FilterConditionType.between:\n      case FilterConditionType.listLength:\n        return 2;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/query_builder/query_group.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/query_builder/query_filter.dart';\n\nclass QueryGroup extends StatelessWidget {\n  const QueryGroup({\n    super.key,\n    required this.collection,\n    required this.group,\n    required this.level,\n    required this.onChanged,\n    this.onDelete,\n  });\n\n  final CollectionSchema<dynamic> collection;\n  final FilterGroup group;\n  final int level;\n  final void Function(FilterGroup group) onChanged;\n  final VoidCallback? onDelete;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return SingleChildScrollView(\n      scrollDirection: Axis.horizontal,\n      physics: const NeverScrollableScrollPhysics(),\n      child: Card(\n        margin: EdgeInsets.zero,\n        elevation: level.toDouble(),\n        child: Padding(\n          padding: const EdgeInsets.all(20),\n          child: IntrinsicHeight(\n            child: Row(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                _Guideline(\n                  group: group,\n                  onChanged: onChanged,\n                  onDelete: onDelete,\n                ),\n                const SizedBox(width: 15),\n                Column(\n                  mainAxisSize: MainAxisSize.min,\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    const SizedBox(height: 10),\n                    if (group.filters.isEmpty)\n                      Padding(\n                        padding: const EdgeInsets.symmetric(vertical: 20),\n                        child: Text(\n                          'Add a filter or nested group to limit the results.\\n'\n                          'Click the group type to change it.',\n                          style: TextStyle(\n                            color: theme.colorScheme.onPrimaryContainer\n                                .withOpacity(0.5),\n                          ),\n                        ),\n                      ),\n                    for (final filter in group.filters) ...[\n                      if (filter is FilterGroup)\n                        QueryGroup(\n                          collection: collection,\n                          group: filter,\n                          level: level + 1,\n                          onChanged: (updated) =>\n                              _performUpdate(add: updated, remove: filter),\n                          onDelete: () => _performUpdate(remove: filter),\n                        )\n                      else\n                        Row(\n                          children: [\n                            QueryFilter(\n                              collection: collection,\n                              condition: filter as FilterCondition,\n                              onChanged: (updated) => _performUpdate(\n                                add: updated,\n                                remove: filter,\n                              ),\n                            ),\n                            const SizedBox(width: 5),\n                            IconButton(\n                              icon: const Icon(Icons.close_rounded, size: 20),\n                              onPressed: () => _performUpdate(remove: filter),\n                            ),\n                          ],\n                        ),\n                      const SizedBox(height: 12),\n                    ],\n                    GroupFilterButton(\n                      idName: collection.idName,\n                      group: group,\n                      level: level,\n                      onAdd: (newFilter) => _performUpdate(add: newFilter),\n                    ),\n                    const SizedBox(height: 10),\n                  ],\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  void _performUpdate({FilterOperation? add, FilterOperation? remove}) {\n    final newFilters = group.filters.toList();\n    if (remove != null) {\n      if (add != null) {\n        newFilters[newFilters.indexOf(remove)] = add;\n      } else {\n        newFilters.remove(remove);\n      }\n    } else if (add != null) {\n      newFilters.add(add);\n    }\n    onChanged(\n      FilterGroup(\n        type: group.type,\n        filters: newFilters,\n      ),\n    );\n  }\n}\n\nclass _Guideline extends StatelessWidget {\n  const _Guideline({\n    required this.group,\n    required this.onChanged,\n    this.onDelete,\n  });\n\n  final FilterGroup group;\n  final void Function(FilterGroup condition) onChanged;\n  final VoidCallback? onDelete;\n\n  @override\n  Widget build(BuildContext context) {\n    final color = group.type.color;\n    return Column(\n      children: [\n        Expanded(\n          child: Container(\n            width: 17.5,\n            margin: const EdgeInsets.only(left: 10),\n            decoration: BoxDecoration(\n              border: Border(\n                top: BorderSide(color: color, width: 2.5),\n                left: BorderSide(color: color, width: 2.5),\n              ),\n            ),\n          ),\n        ),\n        Padding(\n          padding: const EdgeInsets.symmetric(vertical: 5),\n          child: InputChip(\n            backgroundColor: color,\n            deleteIconColor: Colors.white,\n            label: SizedBox(\n              width: 30,\n              child: Center(\n                child: Text(\n                  group.type.name,\n                  style: const TextStyle(\n                    color: Colors.white,\n                    fontWeight: FontWeight.bold,\n                  ),\n                ),\n              ),\n            ),\n            tooltip: 'Change group type',\n            onDeleted: onDelete,\n            onPressed: () {\n              final newType = group.type == FilterGroupType.and\n                  ? FilterGroupType.or\n                  : group.type == FilterGroupType.or\n                      ? FilterGroupType.xor\n                      : FilterGroupType.and;\n              onChanged(\n                FilterGroup(\n                  type: newType,\n                  filters: group.filters,\n                ),\n              );\n            },\n            side: BorderSide.none,\n            shape: RoundedRectangleBorder(\n              borderRadius: BorderRadius.circular(20),\n            ),\n          ),\n        ),\n        Expanded(\n          child: Container(\n            width: 17.5,\n            margin: const EdgeInsets.only(left: 10),\n            decoration: BoxDecoration(\n              border: Border(\n                bottom: BorderSide(color: color, width: 2.5),\n                left: BorderSide(color: color, width: 2.5),\n              ),\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n}\n\nclass GroupFilterButton extends StatelessWidget {\n  const GroupFilterButton({\n    super.key,\n    required this.idName,\n    required this.group,\n    required this.level,\n    required this.onAdd,\n  });\n\n  final String idName;\n  final FilterGroup group;\n  final int level;\n  final void Function(FilterOperation filter) onAdd;\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        ElevatedButton.icon(\n          icon: const Icon(Icons.workspaces_rounded),\n          label: const Text('Add Group'),\n          style: ButtonStyle(\n            elevation: MaterialStateProperty.all(level + 1),\n          ),\n          onPressed: () {\n            onAdd(\n              FilterGroup(\n                type: FilterGroupType.and,\n                filters: [],\n              ),\n            );\n          },\n        ),\n        const SizedBox(width: 20),\n        ElevatedButton.icon(\n          icon: const Icon(Icons.filter_alt_rounded),\n          label: const Text('Add Filter'),\n          style: ButtonStyle(\n            elevation: MaterialStateProperty.all(level + 1),\n          ),\n          onPressed: () {\n            onAdd(\n              FilterCondition.greaterThan(\n                property: idName,\n                value: 0,\n                caseSensitive: false,\n              ),\n            );\n          },\n        ),\n      ],\n    );\n  }\n}\n\nextension on FilterGroupType {\n  String get name => this == FilterGroupType.and\n      ? 'AND'\n      : this == FilterGroupType.or\n          ? 'OR'\n          : 'XOR';\n\n  Color get color => this == FilterGroupType.and\n      ? Colors.blue\n      : this == FilterGroupType.or\n          ? Colors.orange\n          : Colors.green;\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/sidebar.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:isar/isar.dart';\nimport 'package:isar_inspector/collections_list.dart';\nimport 'package:isar_inspector/connect_client.dart';\nimport 'package:isar_inspector/instance_selector.dart';\nimport 'package:isar_inspector/main.dart';\n\nclass Sidebar extends StatelessWidget {\n  const Sidebar({\n    super.key,\n    required this.instances,\n    required this.selectedInstance,\n    required this.onInstanceSelected,\n    required this.collections,\n    required this.collectionInfo,\n    required this.selectedCollection,\n    required this.onCollectionSelected,\n  });\n\n  final List<String> instances;\n  final String selectedInstance;\n  final void Function(String instance) onInstanceSelected;\n\n  final List<CollectionSchema<dynamic>> collections;\n  final Map<String, ConnectCollectionInfo?> collectionInfo;\n  final String? selectedCollection;\n  final void Function(String instance) onCollectionSelected;\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Card(\n      margin: EdgeInsets.zero,\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          SizedBox(\n            height: 80,\n            child: Padding(\n              padding: const EdgeInsets.symmetric(horizontal: 20),\n              child: Row(\n                children: [\n                  Image.asset(\n                    'assets/logo.png',\n                    width: 40,\n                  ),\n                  const SizedBox(width: 15),\n                  Column(\n                    mainAxisSize: MainAxisSize.min,\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: [\n                      Text(\n                        'Isar',\n                        style: theme.textTheme.titleMedium!.copyWith(\n                          fontWeight: FontWeight.bold,\n                        ),\n                      ),\n                      Text(\n                        'Inspector',\n                        style: theme.textTheme.titleMedium!.copyWith(\n                          fontWeight: FontWeight.bold,\n                        ),\n                      ),\n                    ],\n                  ),\n                  const Spacer(),\n                  IconButton(\n                    padding: const EdgeInsets.all(20),\n                    icon: Icon(\n                      theme.brightness == Brightness.light\n                          ? Icons.dark_mode_rounded\n                          : Icons.light_mode_rounded,\n                    ),\n                    onPressed: DarkMode.of(context).toggle,\n                  )\n                ],\n              ),\n            ),\n          ),\n          const SizedBox(height: 20),\n          Expanded(\n            child: Padding(\n              padding: const EdgeInsets.symmetric(horizontal: 15),\n              child: CollectionsList(\n                collections: collections,\n                collectionInfo: collectionInfo,\n                selectedCollection: selectedCollection,\n                onSelected: onCollectionSelected,\n              ),\n            ),\n          ),\n          const SizedBox(height: 12),\n          InstanceSelector(\n            instances: instances,\n            selectedInstance: selectedInstance,\n            onSelected: onInstanceSelected,\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "packages/isar_inspector/lib/util.dart",
    "content": "import 'package:isar/isar.dart';\n\nextension CollectionSchemaX on CollectionSchema<dynamic> {\n  PropertySchema propertyOrId(String name) {\n    if (name == idName) {\n      return PropertySchema(id: 0, name: name, type: IsarType.long);\n    } else {\n      return property(name);\n    }\n  }\n\n  List<PropertySchema> get idAndProperties => [\n        PropertySchema(id: 0, name: idName, type: IsarType.long),\n        ...properties.values,\n      ];\n}\n"
  },
  {
    "path": "packages/isar_inspector/pubspec.yaml",
    "content": "name: isar_inspector\npublish_to: \"none\"\nversion: 1.0.0+1\n\nenvironment:\n  sdk: \">=2.17.0 <3.0.0\"\n\ndependencies:\n  clickup_fading_scroll:\n    git:\n      url: https://github.com/clickup/clickup_fading_scroll\n  flutter:\n    sdk: flutter\n  flutter_svg: any\n  font_awesome_flutter: any\n  go_router: ^5.0.0\n  google_fonts: 6.1.0\n  isar:\n    path: ../isar\n  vm_service: ^11.0.0\n  web_socket_channel: ^2.2.0\n\ndev_dependencies:\n  build_runner: any\n  flutter_lints: ^2.0.1\n  very_good_analysis: ^3.0.1\n\nflutter:\n  uses-material-design: true\n\n  assets:\n    - assets/logo.png\n"
  },
  {
    "path": "packages/isar_inspector/web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <!--\n    If you are serving your web app in a path other than the root, change the\n    href value below to reflect the base path you are serving from.\n\n    The path provided below has to start and end with a slash \"/\" in order for\n    it to work correctly.\n\n    For more details:\n    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base\n\n    This is a placeholder for base href that will be replaced by the value of\n    the `--base-href` argument provided to `flutter build`.\n  -->\n  <base href=\"$FLUTTER_BASE_HREF\">\n\n  <meta charset=\"UTF-8\">\n  <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n  <meta name=\"description\" content=\"Isar Inspector\">\n\n  <!-- iOS meta tags & icons -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"Isar Inspector\">\n  <link rel=\"apple-touch-icon\" href=\"https://isar.dev/icon-256x256.png\">\n\n  <!-- Favicon -->\n  <link rel=\"icon\" type=\"image/png\" href=\"https://isar.dev/icon-256x256.png\" />\n\n  <title>Isar Inspector</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n  <script defer src=\"flutter.js\"></script>\n  <style lang=\"css\">\n    body {\n      background: #1a1c1e;\n    }\n\n    #loader {\n      position: absolute;\n      display: grid;\n      grid-auto-flow: row;\n      row-gap: 20px;\n      justify-items: center;\n      left: 50%;\n      top: 50%;\n      transform: translate(-50%, calc(-50% + 12pt));\n      z-index: 5000;\n    }\n\n    #loader img {\n      width: 120px;\n      animation: cubic-bezier(0.74, 0.03, 0.37, 0.96) spin 1.5s infinite;\n    }\n\n    #loader #loader-info-text {\n      font-family: sans-serif;\n      font-size: 12pt;\n      color: #fff;\n      text-align: center;\n    }\n\n    #loader.hide  {\n      animation: fade .5s;\n    }\n\n    @keyframes spin {\n      0% {\n        transform: rotate(0deg);\n      }\n      90% {\n        transform: rotate(720deg);\n      }\n      100% {\n        transform: rotate(720deg);\n      }\n    }\n\n    @keyframes fade {\n      from {\n        opacity: 1;\n      }\n      to {\n        opacity: 0;\n      }\n    }\n  </style>\n</head>\n\n<body>\n<section id=\"loader\">\n  <img src=\"assets/assets/logo.png\" alt=\"Isar Logo\">\n  <span id=\"loader-info-text\"></span>\n</section>\n  <!-- This script installs service_worker.js to provide PWA functionality to\n       application. For more information, see:\n       https://developers.google.com/web/fundamentals/primers/service-workers -->\n  <script>\n    var serviceWorkerVersion = null;\n\n    window.addEventListener('load', function () {\n      const loadingContainer = document.querySelector('#loader');\n      const loading = document.querySelector('#loader-info-text');\n      loading.textContent = \"Loading entrypoint...\";\n      _flutter.loader.loadEntrypoint({\n        serviceWorker: {\n          serviceWorkerVersion: serviceWorkerVersion,\n        }\n      }).then(function(engineInitializer) {\n        loading.textContent = \"Initializing engine...\";\n        return engineInitializer.initializeEngine();\n      }).then(function(appRunner) {\n        loading.textContent = \"Running app...\";\n        return appRunner.runApp();\n      }).then(function (app) {\n        // Wait a few milliseconds so users can see the \"zoooom\" animation\n        // before getting rid of the \"loading\" div.\n        loadingContainer.classList.add('hide');\n        window.setTimeout(function () {\n          loadingContainer.remove();\n        }, 500);\n      });\n    });\n\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "packages/isar_inspector/web/manifest.json",
    "content": "{\n    \"name\": \"Isar Inspector\",\n    \"short_name\": \"Isar Inspector\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#1a1c1e\",\n    \"theme_color\": \"#0175C2\",\n    \"description\": \"Inspector for the Isar database.\",\n    \"orientation\": \"portrait-primary\",\n    \"prefer_related_applications\": false,\n    \"icons\": [\n        {\n            \"src\": \"https://isar.dev/icon-256x256.png\",\n            \"sizes\": \"256x256\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"https://isar.dev/icon-512x512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        }\n    ]\n}"
  },
  {
    "path": "packages/isar_test/.gitignore",
    "content": "*.g.dart\n*.freezed.dart\nall_tests.dart\n\nfilter_long_test.dart\nfilter_long_list_test.dart\nfilter_double_test.dart\nfilter_double_list_test.dart\n\nwhere_long_test.dart\nwhere_long_list_test.dart\nwhere_double_test.dart\nwhere_double_list_test.dart"
  },
  {
    "path": "packages/isar_test/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b\n  channel: stable\n\nproject_type: app\n"
  },
  {
    "path": "packages/isar_test/README.md",
    "content": "## Isar tests\n\nUse the following commands to run the tests on a connected device:\n\n### Unit tests\n\n```\nsh tool/prepare_tests.sh\nsh tool/build.sh\ndart test\n```\n\n### Integration tests\n\n```\nsh tool/prepare_tests.sh\nsh tool/build.sh\nflutter test integration_test/integration_test.dart\n```\n"
  },
  {
    "path": "packages/isar_test/analysis_options.yaml",
    "content": "include: package:very_good_analysis/analysis_options.yaml\n\nanalyzer:\n  errors:\n    cascade_invocations: ignore\n    avoid_positional_boolean_parameters: ignore\n    parameter_assignments: ignore\n    public_member_api_docs: ignore\n    use_string_buffers: ignore\n    avoid_equals_and_hash_code_on_mutable_classes: ignore\n    "
  },
  {
    "path": "packages/isar_test/android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n"
  },
  {
    "path": "packages/isar_test/android/app/build.gradle",
    "content": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterRoot = localProperties.getProperty('flutter.sdk')\nif (flutterRoot == null) {\n    throw new GradleException(\"Flutter SDK not found. Define location with flutter.sdk in the local.properties file.\")\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply from: \"$flutterRoot/packages/flutter_tools/gradle/flutter.gradle\"\n\nandroid {\n    compileSdkVersion 33\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n\n    defaultConfig {\n        applicationId \"dev.isar.isar_test\"\n        minSdkVersion 16\n        targetSdkVersion 33\n        versionCode flutterVersionCode.toInteger()\n        versionName flutterVersionName\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            signingConfig signingConfigs.debug\n        }\n    }\n}\n\nflutter {\n    source '../..'\n}\n\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.2.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n}\n"
  },
  {
    "path": "packages/isar_test/android/app/src/androidTest/java/dev/isar/isar_test/MainActivityTest.java",
    "content": "package dev.isar.isar_test;\n\nimport androidx.test.rule.ActivityTestRule;\nimport dev.flutter.plugins.integration_test.FlutterTestRunner;\nimport org.junit.Rule;\nimport org.junit.runner.RunWith;\n\n@RunWith(FlutterTestRunner.class)\npublic class MainActivityTest {\n  @Rule\n  public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, true, false);\n}"
  },
  {
    "path": "packages/isar_test/android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"dev.isar.isar_test\">\n   <application\n        android:label=\"isar_test\"\n        android:icon=\"@mipmap/ic_launcher\">\n        <activity\n            android:name=\".MainActivity\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\"\n            android:exported=\"true\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <!-- Displays an Android View that continues showing the launch screen\n                 Drawable until Flutter paints its first frame, then this splash\n                 screen fades out. A splash screen is useful to avoid any visual\n                 gap between the end of Android's launch screen and the painting of\n                 Flutter's first frame. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.SplashScreenDrawable\"\n              android:resource=\"@drawable/launch_background\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "packages/isar_test/android/app/src/main/kotlin/dev/isar/isar_test/MainActivity.kt",
    "content": "package dev.isar.isar_test\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivity: FlutterActivity() {\n}\n"
  },
  {
    "path": "packages/isar_test/android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "packages/isar_test/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "packages/isar_test/android/build.gradle",
    "content": "buildscript {\n    ext.kotlin_version = '1.7.21'\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.3.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "packages/isar_test/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Nov 09 23:26:39 CET 2022\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4-bin.zip\ndistributionPath=wrapper/dists\nzipStorePath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\n"
  },
  {
    "path": "packages/isar_test/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "packages/isar_test/android/settings.gradle",
    "content": "include ':app'\n\ndef localPropertiesFile = new File(rootProject.projectDir, \"local.properties\")\ndef properties = new Properties()\n\nassert localPropertiesFile.exists()\nlocalPropertiesFile.withReader(\"UTF-8\") { reader -> properties.load(reader) }\n\ndef flutterSdkPath = properties.getProperty(\"flutter.sdk\")\nassert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\napply from: \"$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle\"\n"
  },
  {
    "path": "packages/isar_test/android/settings_aar.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "packages/isar_test/integration_test/integration_test.dart",
    "content": "// ignore_for_file: avoid_print, depend_on_referenced_packages\n\nimport 'dart:async';\n\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:integration_test/integration_test.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:path_provider/path_provider.dart';\n\nimport 'all_tests.dart' as tests;\n\nvoid main() async {\n  IntegrationTestWidgetsFlutterBinding.ensureInitialized();\n  final completer = Completer<void>();\n\n  group('Integration test', () {\n    setUpAll(() async {\n      if (!kIsWeb) {\n        final dir = await getTemporaryDirectory();\n        testTempPath = dir.path;\n      }\n    });\n    tearDownAll(() {\n      print('Isar test done');\n      completer.complete();\n    });\n\n    tests.main();\n  });\n\n  testWidgets(\n    'Isar',\n    (t) async {\n      await completer.future;\n      expect(testCount > 0, true);\n      expect(testErrors, isEmpty);\n    },\n    timeout: Timeout.none,\n  );\n}\n"
  },
  {
    "path": "packages/isar_test/ios/.gitignore",
    "content": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "packages/isar_test/ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>9.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/ios/Flutter/Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "packages/isar_test/ios/Flutter/Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "packages/isar_test/ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\n# platform :ios, '9.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_modular_headers!\n\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "packages/isar_test/ios/Runner/AppDelegate.swift",
    "content": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"76x76\",\n      \"idiom\": \"universal\",\n      \"filename\": \"Icon-App-76x76@1x.png\",\n      \"scale\": \"1x\"\n    }\n  ],\n  \"info\": {\n    \"version\": 1,\n    \"author\": \"xcode\"\n  }\n}"
  },
  {
    "path": "packages/isar_test/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"idiom\": \"universal\",\n      \"filename\": \"LaunchImage.png\",\n      \"scale\": \"1x\"\n    }\n  ],\n  \"info\": {\n    \"version\": 1,\n    \"author\": \"xcode\"\n  }\n}"
  },
  {
    "path": "packages/isar_test/ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>isar_test</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n\t\tE699F95AE0837093B173D5D2 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 293477DA8B12E689D9C8A52F /* libPods-Runner.a */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0E0B516E592199B3B7770EDA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t293477DA8B12E689D9C8A52F /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = \"libPods-Runner.a\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tE43DAE9F3D1A82DFDBB1023C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tE952EC9ED57F026DE326B330 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tE699F95AE0837093B173D5D2 /* libPods-Runner.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t3B4FEC22A81DACCF5DBE5D28 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0E0B516E592199B3B7770EDA /* Pods-Runner.debug.xcconfig */,\n\t\t\t\tE952EC9ED57F026DE326B330 /* Pods-Runner.release.xcconfig */,\n\t\t\t\tE43DAE9F3D1A82DFDBB1023C /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\t3B4FEC22A81DACCF5DBE5D28 /* Pods */,\n\t\t\t\tC4CE71333639FFD1671CC258 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC4CE71333639FFD1671CC258 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t293477DA8B12E689D9C8A52F /* libPods-Runner.a */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2A343220D399D020D21882A3 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1300;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t2A343220D399D020D21882A3 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = 3B533462NK;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = dev.isar.integrationTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = 3B533462NK;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = dev.isar.integrationTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = 3B533462NK;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = dev.isar.integrationTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/lib/isar_test.dart",
    "content": "export 'src/common.dart';\nexport 'src/listener.dart';\nexport 'src/matchers.dart';\nexport 'src/sync_async_helper.dart';\nexport 'src/sync_future.dart';\n\nexport 'src/twitter/entities.dart';\nexport 'src/twitter/geo.dart';\nexport 'src/twitter/media.dart';\nexport 'src/twitter/tweet.dart';\nexport 'src/twitter/user.dart';\nexport 'src/twitter/util.dart';\n"
  },
  {
    "path": "packages/isar_test/lib/src/common.dart",
    "content": "// ignore_for_file: implementation_imports\n\nimport 'dart:async';\nimport 'dart:io';\nimport 'dart:math';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/src/init_native.dart'\n    if (dart.library.html) 'package:isar_test/src/init_web.dart';\nimport 'package:isar_test/src/sync_async_helper.dart';\nimport 'package:meta/meta.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:test/test.dart';\nimport 'package:test_api/src/backend/invoker.dart';\n\nconst kIsWeb = identical(0, 0.0);\n\nfinal testErrors = <String>[];\nint testCount = 0;\n\nvar _setUp = false;\nFuture<void> _prepareTest() async {\n  if (!_setUp) {\n    await init();\n    _setUp = true;\n  }\n}\n\n@isTest\nvoid isarTest(\n  String name,\n  dynamic Function() body, {\n  Timeout? timeout,\n  bool skip = false,\n}) {\n  isarTestSync(name, body, timeout: timeout, skip: skip);\n  isarTestAsync(name, body, timeout: timeout, skip: skip);\n}\n\n@isTest\nvoid isarTestSync(\n  String name,\n  dynamic Function() body, {\n  Timeout? timeout,\n  bool skip = false,\n}) {\n  if (!kIsWeb) {\n    _isarTest(name, true, body, timeout: timeout, skip: skip);\n  }\n}\n\n@isTest\nvoid isarTestAsync(\n  String name,\n  dynamic Function() body, {\n  Timeout? timeout,\n  bool skip = false,\n}) {\n  _isarTest(name, false, body, timeout: timeout, skip: skip);\n}\n\nvoid _isarTest(\n  String name,\n  bool syncTest,\n  dynamic Function() body, {\n  Timeout? timeout,\n  bool skip = false,\n}) {\n  final testName = syncTest ? '$name SYNC' : name;\n  test(\n    testName,\n    () async {\n      await runZoned(\n        () async {\n          try {\n            await _prepareTest();\n            await body();\n            testCount++;\n          } catch (e) {\n            testErrors.add('$testName: $e');\n            rethrow;\n          }\n        },\n        zoneValues: {\n          #syncTest: syncTest,\n        },\n      );\n    },\n    timeout: timeout ?? const Timeout(Duration(minutes: 10)),\n    skip: skip,\n  );\n}\n\n@isTest\nvoid isarTestVm(String name, dynamic Function() body) {\n  isarTest(name, body, skip: kIsWeb);\n}\n\n@isTest\nvoid isarTestWeb(String name, dynamic Function() body) {\n  isarTest(name, body, skip: !kIsWeb);\n}\n\nString getRandomName() {\n  final random = Random().nextInt(pow(2, 32) as int).toString();\n  return '${random}_tmp';\n}\n\nString? testTempPath;\nFuture<Isar> openTempIsar(\n  List<CollectionSchema<dynamic>> schemas, {\n  String? name,\n  String? directory,\n  int maxSizeMiB = Isar.defaultMaxSizeMiB,\n  CompactCondition? compactOnLaunch,\n  bool closeAutomatically = true,\n}) async {\n  await _prepareTest();\n  if (!kIsWeb && directory == null && testTempPath == null) {\n    final dartToolDir = path.join(Directory.current.path, '.dart_tool');\n    testTempPath = path.join(dartToolDir, 'test', 'tmp');\n    await Directory(testTempPath!).create(recursive: true);\n  }\n\n  final isar = await tOpen(\n    schemas: schemas,\n    name: name ?? getRandomName(),\n    maxSizeMiB: maxSizeMiB,\n    directory: testTempPath ?? '',\n    compactOnLaunch: compactOnLaunch,\n  );\n\n  if (Invoker.current != null && closeAutomatically) {\n    addTearDown(() async {\n      if (isar.isOpen) {\n        await isar.close(deleteFromDisk: true);\n      }\n    });\n  }\n\n  // ignore: invalid_use_of_visible_for_testing_member\n  if (!kIsWeb) await isar.verify();\n  return isar;\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/init_native.dart",
    "content": "import 'dart:ffi';\nimport 'dart:io';\n\nimport 'package:isar/isar.dart';\nimport 'package:path/path.dart' as path;\n\nFuture<void> init() async {\n  if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) {\n    final rootDir = path.dirname(path.dirname(Directory.current.path));\n    final binaryName = Platform.isWindows\n        ? 'isar.dll'\n        : Platform.isMacOS\n            ? 'libisar.dylib'\n            : 'libisar.so';\n    try {\n      await Isar.initializeIsarCore();\n    } catch (e) {\n      await Isar.initializeIsarCore(\n        libraries: {\n          Abi.macosArm64: path.join(\n            rootDir,\n            'target',\n            'aarch64-apple-darwin',\n            'release',\n            binaryName,\n          ),\n          Abi.macosX64: path.join(\n            rootDir,\n            'target',\n            'x86_64-apple-darwin',\n            'release',\n            binaryName,\n          ),\n          Abi.linuxArm64: path.join(\n            rootDir,\n            'target',\n            'aarch64-unknown-linux-gnu',\n            'release',\n            binaryName,\n          ),\n          Abi.linuxX64: path.join(\n            rootDir,\n            'target',\n            'x86_64-unknown-linux-gnu',\n            'release',\n            binaryName,\n          ),\n          Abi.windowsX64: path.join(\n            rootDir,\n            'target',\n            'x86_64-pc-windows-msvc',\n            'release',\n            binaryName,\n          ),\n        },\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/init_web.dart",
    "content": "// ignore_for_file: avoid_web_libraries_in_flutter, implementation_imports\n\nimport 'dart:js' as js;\n\nimport 'package:isar/src/web/open.dart' as isar_web;\nimport 'package:isar_test/src/isar_web_src.dart';\n\nFuture<void> init() async {\n  js.context.callMethod('eval', [isarWebSrc]);\n  // ignore: invalid_use_of_visible_for_testing_member\n  isar_web.doNotInitializeIsarWeb();\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/isar_web_src.dart",
    "content": "const isarWebSrc = '';\n"
  },
  {
    "path": "packages/isar_test/lib/src/listener.dart",
    "content": "import 'dart:async';\n\nimport 'package:test/test.dart';\n\nclass Listener<T> {\n  Listener(Stream<T> stream) {\n    subscription = stream.listen((event) {\n      if (_completer != null) {\n        _completer!.complete(event);\n        _completer = null;\n      } else {\n        _unprocessed.add(event);\n      }\n    });\n  }\n  late StreamSubscription<void> subscription;\n  final List<T> _unprocessed = <T>[];\n  Completer<T>? _completer;\n\n  Future<T> get next {\n    if (_unprocessed.isEmpty) {\n      expect(_completer, null);\n      _completer = Completer<T>();\n      return _completer!.future;\n    } else {\n      return Future.value(_unprocessed.removeAt(0));\n    }\n  }\n\n  Future<void> done() async {\n    await Future<void>.delayed(const Duration(milliseconds: 100));\n    await subscription.cancel();\n    expect(_completer, null);\n    expect(_unprocessed, <dynamic>[]);\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/matchers.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/src/sync_async_helper.dart';\nimport 'package:test/test.dart';\n\nFuture<void> qEqualSet<T>(\n  QueryBuilder<dynamic, T, QQueryOperations> query,\n  Iterable<T> target,\n) async {\n  final results = (await query.tFindAll()).toList();\n  expect(results.toSet(), target.toSet());\n}\n\nFuture<void> qEqual<T>(\n  QueryBuilder<dynamic, T, QQueryOperations> query,\n  List<T> target,\n) async {\n  final results = (await query.tFindAll()).toList();\n  await qEqualSync(results, target);\n}\n\nFuture<void> qEqualSync<T>(List<T> actual, List<T> target) async {\n  if (actual is List<double?>) {\n    for (var i = 0; i < actual.length; i++) {\n      expect(doubleListEquals(actual.cast(), target.cast()), true);\n    }\n  } else if (actual is List<List<double?>?>) {\n    for (var i = 0; i < actual.length; i++) {\n      doubleListEquals(\n        actual[i] as List<double?>?,\n        target[i] as List<double?>?,\n      );\n    }\n  } else {\n    expect(actual, target);\n  }\n}\n\nbool doubleListEquals(List<double?>? l1, List<double?>? l2) {\n  if (l1?.length != l2?.length) {\n    return false;\n  }\n  if (l1 != null && l2 != null) {\n    for (var i = 0; i < l1.length; i++) {\n      if (!doubleEquals(l1[i], l2[i])) {\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nbool doubleEquals(double? d1, double? d2) {\n  return d1 == d2 ||\n      (d1 != null &&\n          d2 != null &&\n          ((d1.isNaN && d2.isNaN) || (d1 - d2).abs() < 0.001));\n}\n\nMatcher isIsarError([String? contains]) {\n  return allOf(\n    isA<IsarError>(),\n    predicate(\n      (IsarError e) =>\n          contains == null ||\n          e.toString().toLowerCase().contains(contains.toLowerCase()),\n    ),\n  );\n}\n\nMatcher throwsIsarError([String? contains]) {\n  return throwsA(isIsarError(contains));\n}\n\nMatcher get throwsAssertionError {\n  var matcher = anything;\n  assert(\n    () {\n      matcher = throwsA(isA<AssertionError>());\n      return true;\n    }(),\n    'only in debug mode',\n  );\n  return matcher;\n}\n\nbool listEquals<T>(List<T>? a, List<T>? b) {\n  if (a == null) {\n    return b == null;\n  }\n  if (b == null || a.length != b.length) {\n    return false;\n  }\n  if (identical(a, b)) {\n    return true;\n  }\n  for (var index = 0; index < a.length; index += 1) {\n    if (a[index] != b[index]) {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool dateTimeListEquals(List<dynamic>? a, List<dynamic>? b) {\n  assert(\n    (a == null || a.every((e) => e == null || e is DateTime)) &&\n        (b == null || b.every((e) => e == null || e is DateTime)),\n    'Parameters must be lists of `DateTime` or `DateTime?`',\n  );\n\n  return listEquals(\n    a?.cast<DateTime?>().map((e) => e?.toUtc()).toList(),\n    b?.cast<DateTime?>().map((e) => e?.toUtc()).toList(),\n  );\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/sync_async_helper.dart",
    "content": "import 'dart:async';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/src/sync_future.dart';\n\nbool get syncTest => Zone.current[#syncTest] as bool? ?? false;\n\nFuture<Isar> tOpen({\n  required List<CollectionSchema<dynamic>> schemas,\n  required String directory,\n  String name = Isar.defaultName,\n  int maxSizeMiB = Isar.defaultMaxSizeMiB,\n  bool relaxedDurability = true,\n  CompactCondition? compactOnLaunch,\n}) {\n  if (syncTest) {\n    final isar = Isar.openSync(\n      schemas,\n      directory: directory,\n      name: name,\n      maxSizeMiB: maxSizeMiB,\n      relaxedDurability: relaxedDurability,\n      compactOnLaunch: compactOnLaunch,\n      inspector: false,\n    );\n    return SynchronousFuture(isar);\n  } else {\n    return Isar.open(\n      schemas,\n      directory: directory,\n      name: name,\n      maxSizeMiB: maxSizeMiB,\n      relaxedDurability: relaxedDurability,\n      compactOnLaunch: compactOnLaunch,\n      inspector: false,\n    );\n  }\n}\n\nextension TIsar on Isar {\n  Future<T> tTxn<T>(Future<T> Function() callback) {\n    if (syncTest) {\n      return Future.value(txnSync(callback));\n    } else {\n      return txn(callback);\n    }\n  }\n\n  Future<T> tWriteTxn<T>(Future<T> Function() callback, {bool silent = false}) {\n    if (syncTest) {\n      return writeTxnSync(callback, silent: silent);\n    } else {\n      return writeTxn(callback, silent: silent);\n    }\n  }\n\n  Future<void> tClear() {\n    if (syncTest) {\n      clearSync();\n      return SynchronousFuture(null);\n    } else {\n      return clear();\n    }\n  }\n}\n\nextension TIsarCollection<OBJ> on IsarCollection<OBJ> {\n  Future<OBJ?> tGet(Id id) {\n    if (syncTest) {\n      return SynchronousFuture(getSync(id));\n    } else {\n      return get(id);\n    }\n  }\n\n  Future<List<OBJ?>> tGetAll(List<Id> ids) {\n    if (syncTest) {\n      return SynchronousFuture(getAllSync(ids));\n    } else {\n      return getAll(ids);\n    }\n  }\n\n  Future<Id> tPut(OBJ object, {bool saveLinks = false}) {\n    if (syncTest) {\n      return SynchronousFuture(putSync(object, saveLinks: saveLinks));\n    } else {\n      return put(object);\n    }\n  }\n\n  Future<List<Id>> tPutAll(List<OBJ> objects, {bool saveLinks = false}) {\n    if (syncTest) {\n      return SynchronousFuture(putAllSync(objects, saveLinks: saveLinks));\n    } else {\n      return putAll(objects);\n    }\n  }\n\n  Future<bool> tDelete(Id id) {\n    if (syncTest) {\n      return SynchronousFuture(deleteSync(id));\n    } else {\n      return delete(id);\n    }\n  }\n\n  Future<int> tDeleteAll(List<Id> ids) {\n    if (syncTest) {\n      return SynchronousFuture(deleteAllSync(ids));\n    } else {\n      return deleteAll(ids);\n    }\n  }\n\n  Future<void> tClear() {\n    if (syncTest) {\n      clearSync();\n      return SynchronousFuture(null);\n    } else {\n      return clear();\n    }\n  }\n\n  Future<void> tImportJsonRaw(Uint8List jsonBytes) {\n    if (syncTest) {\n      importJsonRawSync(jsonBytes);\n      return SynchronousFuture(null);\n    } else {\n      return importJsonRaw(jsonBytes);\n    }\n  }\n\n  Future<void> tImportJson(List<Map<String, dynamic>> json) {\n    if (syncTest) {\n      importJsonSync(json);\n      return SynchronousFuture(null);\n    } else {\n      return importJson(json);\n    }\n  }\n\n  Future<int> tGetSize({\n    bool includeIndexes = false,\n    bool includeLinks = false,\n  }) {\n    if (syncTest) {\n      return SynchronousFuture(\n        getSizeSync(\n          includeIndexes: includeIndexes,\n          includeLinks: includeLinks,\n        ),\n      );\n    } else {\n      return getSize(\n        includeIndexes: includeIndexes,\n        includeLinks: includeLinks,\n      );\n    }\n  }\n}\n\nextension QueryBuilderExecute<OBJ, R>\n    on QueryBuilder<OBJ, R, QQueryOperations> {\n  Future<R?> tFindFirst() {\n    if (syncTest) {\n      return SynchronousFuture(findFirstSync());\n    } else {\n      return findFirst();\n    }\n  }\n\n  Future<List<R>> tFindAll() {\n    if (syncTest) {\n      return SynchronousFuture(findAllSync());\n    } else {\n      return findAll();\n    }\n  }\n\n  Future<int> tCount() {\n    if (syncTest) {\n      return SynchronousFuture(countSync());\n    } else {\n      return count();\n    }\n  }\n\n  Future<bool> tIsEmpty() {\n    if (syncTest) {\n      return SynchronousFuture(isEmptySync());\n    } else {\n      return isEmpty();\n    }\n  }\n\n  Future<bool> tIsNotEmpty() {\n    if (syncTest) {\n      return SynchronousFuture(isNotEmptySync());\n    } else {\n      return isNotEmpty();\n    }\n  }\n\n  Future<bool> tDeleteFirst() {\n    if (syncTest) {\n      return SynchronousFuture(deleteFirstSync());\n    } else {\n      return deleteFirst();\n    }\n  }\n\n  Future<int> tDeleteAll() {\n    if (syncTest) {\n      return SynchronousFuture(deleteAllSync());\n    } else {\n      return deleteAll();\n    }\n  }\n\n  Future<M> tExportJsonRaw<M>(M Function(Uint8List) callback) {\n    if (syncTest) {\n      return SynchronousFuture(exportJsonRawSync(callback));\n    } else {\n      return exportJsonRaw(callback);\n    }\n  }\n\n  Future<List<Map<String, dynamic>>> tExportJson() {\n    if (syncTest) {\n      return SynchronousFuture(exportJsonSync());\n    } else {\n      return exportJson();\n    }\n  }\n}\n\n/// Extension for QueryBuilders\nextension QueryExecuteAggregation<OBJ, T extends num>\n    on QueryBuilder<OBJ, T?, QQueryOperations> {\n  Future<T?> tMin() {\n    if (syncTest) {\n      return SynchronousFuture(minSync());\n    } else {\n      return min();\n    }\n  }\n\n  Future<T?> tMax() {\n    if (syncTest) {\n      return SynchronousFuture(maxSync());\n    } else {\n      return max();\n    }\n  }\n\n  Future<double> tAverage() {\n    if (syncTest) {\n      return SynchronousFuture(averageSync());\n    } else {\n      return average();\n    }\n  }\n\n  Future<T> tSum() {\n    if (syncTest) {\n      return SynchronousFuture(sumSync());\n    } else {\n      return sum();\n    }\n  }\n}\n\n/// Extension for QueryBuilders\nextension QueryExecuteDateAggregation<OBJ>\n    on QueryBuilder<OBJ, DateTime?, QQueryOperations> {\n  Future<DateTime?> tMin() {\n    if (syncTest) {\n      return SynchronousFuture(minSync());\n    } else {\n      return min();\n    }\n  }\n\n  Future<DateTime?> tMax() {\n    if (syncTest) {\n      return SynchronousFuture(maxSync());\n    } else {\n      return max();\n    }\n  }\n}\n\nextension TIsarLinkBase<OBJ> on IsarLinkBase<OBJ> {\n  Future<void> tLoad() {\n    if (syncTest) {\n      loadSync();\n      return SynchronousFuture(null);\n    } else {\n      return load();\n    }\n  }\n\n  Future<void> tSave() {\n    if (syncTest) {\n      saveSync();\n      return SynchronousFuture(null);\n    } else {\n      return save();\n    }\n  }\n\n  Future<void> tReset() {\n    if (syncTest) {\n      resetSync();\n      return SynchronousFuture(null);\n    } else {\n      return reset();\n    }\n  }\n}\n\nextension TIsarLinks<OBJ> on IsarLinks<OBJ> {\n  Future<void> tLoad({bool overrideChanges = false}) {\n    if (syncTest) {\n      loadSync(overrideChanges: overrideChanges);\n      return SynchronousFuture(null);\n    } else {\n      return load(overrideChanges: overrideChanges);\n    }\n  }\n\n  Future<void> tUpdate({\n    List<OBJ> link = const [],\n    List<OBJ> unlink = const [],\n  }) {\n    if (syncTest) {\n      updateSync(link: link, unlink: unlink);\n      return SynchronousFuture(null);\n    } else {\n      return update(link: link, unlink: unlink);\n    }\n  }\n\n  Future<int> tCount() => filter().tCount();\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/sync_future.dart",
    "content": "import 'dart:async';\n\nclass SynchronousFuture<T> implements Future<T> {\n  SynchronousFuture(this._value);\n\n  final T _value;\n\n  @override\n  Stream<T> asStream() {\n    final controller = StreamController<T>();\n    controller.add(_value);\n    controller.close();\n    return controller.stream;\n  }\n\n  @override\n  Future<T> catchError(Function onError, {bool Function(Object error)? test}) =>\n      Completer<T>().future;\n\n  @override\n  Future<R> then<R>(\n    FutureOr<R> Function(T value) onValue, {\n    Function? onError,\n  }) {\n    final dynamic result = onValue(_value);\n    if (result is Future<R>) {\n      return result;\n    }\n    return SynchronousFuture<R>(result as R);\n  }\n\n  @override\n  Future<T> timeout(Duration timeLimit, {FutureOr<T> Function()? onTimeout}) {\n    return Future<T>.value(_value).timeout(timeLimit, onTimeout: onTimeout);\n  }\n\n  @override\n  Future<T> whenComplete(FutureOr<dynamic> Function() action) {\n    try {\n      final result = action();\n      if (result is Future) {\n        return result.then<T>((dynamic value) => _value);\n      }\n      return this;\n    } catch (e, stack) {\n      return Future<T>.error(e, stack);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/twitter/entities.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:json_annotation/json_annotation.dart';\n\nimport 'media.dart';\nimport 'util.dart';\n\npart 'entities.g.dart';\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Entities {\n  Entities();\n\n  factory Entities.fromJson(Map<String, dynamic> json) =>\n      _$EntitiesFromJson(json);\n\n  List<Hashtag>? hashtags;\n\n  List<Media>? media;\n\n  List<Url>? urls;\n\n  List<UserMention>? userMentions;\n\n  List<Symbol>? symbols;\n\n  List<Poll>? polls;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Hashtag {\n  Hashtag();\n\n  factory Hashtag.fromJson(Map<String, dynamic> json) =>\n      _$HashtagFromJson(json);\n\n  List<int>? indices;\n\n  String? text;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Poll {\n  Poll();\n\n  factory Poll.fromJson(Map<String, dynamic> json) => _$PollFromJson(json);\n\n  List<Option>? options;\n\n  @JsonKey(fromJson: convertTwitterDateTime)\n  DateTime? endDatetime;\n\n  String? durationMinutes;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Option {\n  Option();\n\n  factory Option.fromJson(Map<String, dynamic> json) => _$OptionFromJson(json);\n\n  int? position;\n\n  String? text;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Symbol {\n  Symbol();\n\n  factory Symbol.fromJson(Map<String, dynamic> json) => _$SymbolFromJson(json);\n\n  List<int>? indices;\n\n  String? text;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Url {\n  Url();\n\n  factory Url.fromJson(Map<String, dynamic> json) => _$UrlFromJson(json);\n\n  String? displayUrl;\n\n  String? expandedUrl;\n\n  List<int>? indices;\n\n  String? url;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass UserMention {\n  UserMention();\n\n  factory UserMention.fromJson(Map<String, dynamic> json) =>\n      _$UserMentionFromJson(json);\n\n  String? idStr;\n\n  List<int>? indices;\n\n  String? name;\n\n  String? screenName;\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/twitter/geo.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:json_annotation/json_annotation.dart';\n\npart 'geo.g.dart';\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Place {\n  Place();\n\n  factory Place.fromJson(Map<String, dynamic> json) => _$PlaceFromJson(json);\n\n  String? id;\n\n  String? url;\n\n  @Enumerated(EnumType.name)\n  PlaceType? placeType;\n\n  String? name;\n\n  String? fullName;\n\n  String? countryCode;\n\n  String? country;\n}\n\nenum PlaceType {\n  admin,\n  country,\n  city,\n  poi,\n  neighborhood;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Coordinates {\n  Coordinates();\n\n  factory Coordinates.fromJson(Map<String, dynamic> json) =>\n      _$CoordinatesFromJson(json);\n\n  List<double>? coordinates;\n\n  String? type;\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/twitter/media.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:json_annotation/json_annotation.dart';\n\npart 'media.g.dart';\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Media {\n  Media();\n\n  factory Media.fromJson(Map<String, dynamic> json) => _$MediaFromJson(json);\n\n  String? displayUrl;\n\n  String? expandedUrl;\n\n  String? idStr;\n\n  List<int>? indices;\n\n  String? mediaUrl;\n\n  String? mediaUrlHttps;\n\n  Sizes? sizes;\n\n  String? sourceStatusIdStr;\n\n  String? type;\n\n  String? url;\n\n  VideoInfo? videoInfo;\n\n  AdditionalMediaInfo? additionalMediaInfo;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Sizes {\n  Sizes();\n\n  factory Sizes.fromJson(Map<String, dynamic> json) => _$SizesFromJson(json);\n\n  Size? thumb;\n\n  Size? medium;\n\n  Size? small;\n\n  Size? large;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Size {\n  Size();\n\n  factory Size.fromJson(Map<String, dynamic> json) => _$SizeFromJson(json);\n\n  int? w;\n\n  int? h;\n\n  String? resize;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass AdditionalMediaInfo {\n  AdditionalMediaInfo();\n\n  factory AdditionalMediaInfo.fromJson(Map<String, dynamic> json) =>\n      _$AdditionalMediaInfoFromJson(json);\n\n  String? title;\n\n  String? description;\n\n  bool? embeddable;\n\n  bool? monetizable;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass VideoInfo {\n  VideoInfo();\n\n  factory VideoInfo.fromJson(Map<String, dynamic> json) =>\n      _$VideoInfoFromJson(json);\n\n  List<int>? aspectRatio;\n\n  int? durationMillis;\n\n  List<Variant>? variants;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass Variant {\n  Variant();\n\n  factory Variant.fromJson(Map<String, dynamic> json) =>\n      _$VariantFromJson(json);\n\n  int? bitrate;\n\n  String? contentType;\n\n  String? url;\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/twitter/tweet.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/src/twitter/entities.dart';\nimport 'package:isar_test/src/twitter/geo.dart';\nimport 'package:isar_test/src/twitter/media.dart';\nimport 'package:isar_test/src/twitter/user.dart';\nimport 'package:isar_test/src/twitter/util.dart';\nimport 'package:json_annotation/json_annotation.dart';\n\npart 'tweet.g.dart';\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@collection\nclass Tweet {\n  Tweet();\n\n  factory Tweet.fromJson(Map<String, dynamic> json) => _$TweetFromJson(json);\n\n  Id? isarId;\n\n  @JsonKey(fromJson: convertTwitterDateTime)\n  DateTime? createdAt;\n\n  String? idStr;\n\n  String? source;\n\n  bool? truncated;\n\n  String? inReplyToStatusIdStr;\n\n  String? inReplyToUserIdStr;\n\n  String? inReplyToScreenName;\n\n  User? user;\n\n  Coordinates? coordinates;\n\n  Place? place;\n\n  String? quotedStatusIdStr;\n\n  bool? isQuoteStatus;\n\n  int? quoteCount;\n\n  int? replyCount;\n\n  int? retweetCount;\n\n  int? favoriteCount;\n\n  Entities? entities;\n\n  Entities? extendedEntities;\n\n  bool? favorited;\n\n  bool? retweeted;\n\n  bool? possiblySensitive;\n\n  bool? possiblySensitiveAppealable;\n\n  CurrentUserRetweet? currentUserRetweet;\n\n  String? lang;\n\n  QuotedStatusPermalink? quotedStatusPermalink;\n\n  String? fullText;\n\n  List<int>? displayTextRange;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass CurrentUserRetweet {\n  CurrentUserRetweet();\n\n  factory CurrentUserRetweet.fromJson(Map<String, dynamic> json) =>\n      _$CurrentUserRetweetFromJson(json);\n\n  String? idStr;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass QuotedStatusPermalink {\n  QuotedStatusPermalink();\n\n  factory QuotedStatusPermalink.fromJson(Map<String, dynamic> json) =>\n      _$QuotedStatusPermalinkFromJson(json);\n\n  String? url;\n\n  String? expanded;\n\n  String? display;\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/twitter/user.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/src/twitter/entities.dart';\nimport 'package:isar_test/src/twitter/util.dart';\nimport 'package:json_annotation/json_annotation.dart';\n\npart 'user.g.dart';\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass User {\n  User();\n\n  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);\n\n  String? idStr;\n\n  String? name;\n\n  String? screenName;\n\n  String? location;\n\n  String? url;\n\n  UserEntities? entities;\n\n  String? description;\n\n  bool? protected;\n\n  bool? verified;\n\n  int? followersCount;\n\n  int? friendsCount;\n\n  int? listedCount;\n\n  int? favoritesCount;\n\n  int? statusesCount;\n\n  @JsonKey(fromJson: convertTwitterDateTime)\n  DateTime? createdAt;\n\n  String? profileBannerUrl;\n\n  String? profileImageUrlHttps;\n\n  bool? defaultProfile;\n\n  bool? defaultProfileImage;\n\n  List<String>? withheldInCountries;\n\n  String? withheldScope;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass UserEntities {\n  UserEntities();\n\n  factory UserEntities.fromJson(Map<String, dynamic> json) =>\n      _$UserEntitiesFromJson(json);\n\n  UserEntityUrl? url;\n\n  UserEntityUrl? description;\n}\n\n@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)\n@embedded\nclass UserEntityUrl {\n  UserEntityUrl();\n\n  factory UserEntityUrl.fromJson(Map<String, dynamic> json) =>\n      _$UserEntityUrlFromJson(json);\n\n  List<Url>? urls;\n}\n"
  },
  {
    "path": "packages/isar_test/lib/src/twitter/util.dart",
    "content": "import 'package:intl/intl.dart';\n\nDateTime? convertTwitterDateTime(String? twitterDateString) {\n  if (twitterDateString == null) {\n    return null;\n  }\n\n  try {\n    return DateTime.parse(twitterDateString);\n  } catch (e) {\n    try {\n      final dateString = formatTwitterDateString(twitterDateString);\n      return DateFormat('E MMM dd HH:mm:ss yyyy', 'en_US')\n          .parse(dateString, true);\n    } catch (e) {\n      return null;\n    }\n  }\n}\n\nString formatTwitterDateString(String twitterDateString) {\n  final sanitized = twitterDateString.split(' ')\n    ..removeWhere((part) => part.startsWith('+'));\n\n  return sanitized.join(' ');\n}\n"
  },
  {
    "path": "packages/isar_test/linux/.gitignore",
    "content": "flutter/ephemeral\n"
  },
  {
    "path": "packages/isar_test/linux/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(runner LANGUAGES CXX)\n\nset(BINARY_NAME \"isar_test\")\nset(APPLICATION_ID \"dev.isar.isar_test\")\n\ncmake_policy(SET CMP0063 NEW)\n\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Root filesystem for cross-building.\nif(FLUTTER_TARGET_PLATFORM_SYSROOT)\n  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nendif()\n\n# Configure build options.\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n    STRING \"Flutter build mode\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Profile\" \"Release\")\nendif()\n\n# Compilation settings that should be applied to most targets.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_14)\n  target_compile_options(${TARGET} PRIVATE -Wall -Werror)\n  target_compile_options(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:-O3>\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:NDEBUG>\")\nendfunction()\n\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\n\n# Flutter library and tool build rules.\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\n\nadd_definitions(-DAPPLICATION_ID=\"${APPLICATION_ID}\")\n\n# Application build\nadd_executable(${BINARY_NAME}\n  \"main.cc\"\n  \"my_application.cc\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n)\napply_standard_settings(${BINARY_NAME})\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter)\ntarget_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n# Only the install-generated bundle's copy of the executable will launch\n# correctly, since the resources must in the right relative locations. To avoid\n# people trying to run the unbundled copy, put it in a subdirectory instead of\n# the default top-level location.\nset_target_properties(${BINARY_NAME}\n  PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/intermediates_do_not_run\"\n)\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# By default, \"installing\" just makes a relocatable bundle in the build\n# directory.\nset(BUILD_BUNDLE_DIR \"${PROJECT_BINARY_DIR}/bundle\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\n# Start with a clean build bundle directory every time.\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${BUILD_BUNDLE_DIR}/\\\")\n  \" COMPONENT Runtime)\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}/lib\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\nif(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\n  install(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n"
  },
  {
    "path": "packages/isar_test/linux/flutter/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\n\n# Serves the same purpose as list(TRANSFORM ... PREPEND ...),\n# which isn't available in 3.10.\nfunction(list_prepend LIST_NAME PREFIX)\n    set(NEW_LIST \"\")\n    foreach(element ${${LIST_NAME}})\n        list(APPEND NEW_LIST \"${PREFIX}${element}\")\n    endforeach(element)\n    set(${LIST_NAME} \"${NEW_LIST}\" PARENT_SCOPE)\nendfunction()\n\n# === Flutter Library ===\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\npkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)\npkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)\n\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/libflutter_linux_gtk.so\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/lib/libapp.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"fl_basic_message_channel.h\"\n  \"fl_binary_codec.h\"\n  \"fl_binary_messenger.h\"\n  \"fl_dart_project.h\"\n  \"fl_engine.h\"\n  \"fl_json_message_codec.h\"\n  \"fl_json_method_codec.h\"\n  \"fl_message_codec.h\"\n  \"fl_method_call.h\"\n  \"fl_method_channel.h\"\n  \"fl_method_codec.h\"\n  \"fl_method_response.h\"\n  \"fl_plugin_registrar.h\"\n  \"fl_plugin_registry.h\"\n  \"fl_standard_message_codec.h\"\n  \"fl_standard_method_codec.h\"\n  \"fl_string_codec.h\"\n  \"fl_value.h\"\n  \"fl_view.h\"\n  \"flutter_linux.h\"\n)\nlist_prepend(FLUTTER_LIBRARY_HEADERS \"${EPHEMERAL_DIR}/flutter_linux/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}\")\ntarget_link_libraries(flutter INTERFACE\n  PkgConfig::GTK\n  PkgConfig::GLIB\n  PkgConfig::GIO\n)\nadd_dependencies(flutter flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CMAKE_CURRENT_BINARY_DIR}/_phony_\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh\"\n      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n)\n"
  },
  {
    "path": "packages/isar_test/linux/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <isar_flutter_libs/isar_flutter_libs_plugin.h>\n\nvoid fl_register_plugins(FlPluginRegistry* registry) {\n  g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"IsarFlutterLibsPlugin\");\n  isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);\n}\n"
  },
  {
    "path": "packages/isar_test/linux/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter_linux/flutter_linux.h>\n\n// Registers Flutter plugins.\nvoid fl_register_plugins(FlPluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "packages/isar_test/linux/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  isar_flutter_libs\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "packages/isar_test/linux/main.cc",
    "content": "#include \"my_application.h\"\n\nint main(int argc, char** argv) {\n  g_autoptr(MyApplication) app = my_application_new();\n  return g_application_run(G_APPLICATION(app), argc, argv);\n}\n"
  },
  {
    "path": "packages/isar_test/linux/my_application.cc",
    "content": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nstruct _MyApplication {\n  GtkApplication parent_instance;\n  char** dart_entrypoint_arguments;\n};\n\nG_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)\n\n// Implements GApplication::activate.\nstatic void my_application_activate(GApplication* application) {\n  MyApplication* self = MY_APPLICATION(application);\n  GtkWindow* window =\n      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));\n\n  // Use a header bar when running in GNOME as this is the common style used\n  // by applications and is the setup most users will be using (e.g. Ubuntu\n  // desktop).\n  // If running on X and not using GNOME then just use a traditional title bar\n  // in case the window manager does more exotic layout, e.g. tiling.\n  // If running on Wayland assume the header bar will work (may need changing\n  // if future cases occur).\n  gboolean use_header_bar = TRUE;\n#ifdef GDK_WINDOWING_X11\n  GdkScreen* screen = gtk_window_get_screen(window);\n  if (GDK_IS_X11_SCREEN(screen)) {\n    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);\n    if (g_strcmp0(wm_name, \"GNOME Shell\") != 0) {\n      use_header_bar = FALSE;\n    }\n  }\n#endif\n  if (use_header_bar) {\n    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());\n    gtk_widget_show(GTK_WIDGET(header_bar));\n    gtk_header_bar_set_title(header_bar, \"isar_test\");\n    gtk_header_bar_set_show_close_button(header_bar, TRUE);\n    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));\n  } else {\n    gtk_window_set_title(window, \"isar_test\");\n  }\n\n  gtk_window_set_default_size(window, 1280, 720);\n  gtk_widget_show(GTK_WIDGET(window));\n\n  g_autoptr(FlDartProject) project = fl_dart_project_new();\n  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);\n\n  FlView* view = fl_view_new(project);\n  gtk_widget_show(GTK_WIDGET(view));\n  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));\n\n  fl_register_plugins(FL_PLUGIN_REGISTRY(view));\n\n  gtk_widget_grab_focus(GTK_WIDGET(view));\n}\n\n// Implements GApplication::local_command_line.\nstatic gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {\n  MyApplication* self = MY_APPLICATION(application);\n  // Strip out the first argument as it is the binary name.\n  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);\n\n  g_autoptr(GError) error = nullptr;\n  if (!g_application_register(application, nullptr, &error)) {\n     g_warning(\"Failed to register: %s\", error->message);\n     *exit_status = 1;\n     return TRUE;\n  }\n\n  g_application_activate(application);\n  *exit_status = 0;\n\n  return TRUE;\n}\n\n// Implements GObject::dispose.\nstatic void my_application_dispose(GObject* object) {\n  MyApplication* self = MY_APPLICATION(object);\n  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);\n  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);\n}\n\nstatic void my_application_class_init(MyApplicationClass* klass) {\n  G_APPLICATION_CLASS(klass)->activate = my_application_activate;\n  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;\n  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;\n}\n\nstatic void my_application_init(MyApplication* self) {}\n\nMyApplication* my_application_new() {\n  return MY_APPLICATION(g_object_new(my_application_get_type(),\n                                     \"application-id\", APPLICATION_ID,\n                                     \"flags\", G_APPLICATION_NON_UNIQUE,\n                                     nullptr));\n}\n"
  },
  {
    "path": "packages/isar_test/linux/my_application.h",
    "content": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,\n                     GtkApplication)\n\n/**\n * my_application_new:\n *\n * Creates a new Flutter-based application.\n *\n * Returns: a new #MyApplication.\n */\nMyApplication* my_application_new();\n\n#endif  // FLUTTER_MY_APPLICATION_H_\n"
  },
  {
    "path": "packages/isar_test/macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/xcuserdata/\n"
  },
  {
    "path": "packages/isar_test/macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "packages/isar_test/macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "packages/isar_test/macos/Podfile",
    "content": "platform :osx, '10.11'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\n@NSApplicationMain\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"256x256\",\n      \"idiom\": \"mac\",\n      \"filename\": \"app_icon_256.png\",\n      \"scale\": \"1x\"\n    }\n  ],\n  \"info\": {\n    \"version\": 1,\n    \"author\": \"xcode\"\n  }\n}"
  },
  {
    "path": "packages/isar_test/macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = isar_test\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = dev.isar.isarTest\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2021 dev.isar. All rights reserved.\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController.init()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\n\n    RegisterGeneratedPlugins(registry: flutterViewController)\n\n    super.awakeFromNib()\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 51;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n\t\t9CCA916EB137FBD1D109F602 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49C835836AEEE92C7FEA48A /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0E37D9253505467070F03997 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* isar_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = isar_test.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t3AE173F09D931915B551B258 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\tC49C835836AEEE92C7FEA48A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD8DCD59F510E1985D6ACBCF6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9CCA916EB137FBD1D109F602 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t0FE30314CB0DAD905F8EF6A6 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD8DCD59F510E1985D6ACBCF6 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t0E37D9253505467070F03997 /* Pods-Runner.release.xcconfig */,\n\t\t\t\t3AE173F09D931915B551B258 /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tname = Pods;\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\t0FE30314CB0DAD905F8EF6A6 /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* isar_test.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC49C835836AEEE92C7FEA48A /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tB49BC79186E5C64BFA4E5B73 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t\t5817EC31F907BDB00D965BD4 /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* isar_test.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 0930;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n\t\t5817EC31F907BDB00D965BD4 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tB49BC79186E5C64BFA4E5B73 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.11;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.11;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.11;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "packages/isar_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1000\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"isar_test.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"isar_test.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"isar_test.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"isar_test.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "packages/isar_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "packages/isar_test/pubspec.yaml",
    "content": "name: isar_test\n\npublish_to: \"none\"\n\nversion: 1.0.0+1\n\nenvironment:\n  sdk: \">=2.17.0 <3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  integration_test:\n    sdk: flutter\n  intl: ^0.19.0\n  isar: \n    version: any\n    hosted: https://pub.isar-community.dev\n  isar_flutter_libs: \n    version: any\n    hosted: https://pub.isar-community.dev\n  json_annotation: ^4.7.0\n  meta: ^1.8.0\n  path: ^1.8.2\n  path_provider: ^2.0.10\n  test: ^1.21.4\n  test_api: ^0.5.1\n\ndev_dependencies:\n  build_runner: any\n  flutter_test:\n    sdk: flutter\n  isar_generator: \n    version: any\n    hosted: https://pub.isar-community.dev\n  json_serializable: ^6.3.1\n  very_good_analysis: ^3.0.1\n\ndependency_overrides:\n  isar:\n    path: ../isar\n  isar_flutter_libs:\n    path: ../isar_flutter_libs\n  isar_generator:\n    path: ../isar_generator"
  },
  {
    "path": "packages/isar_test/test/clear_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'clear_test.g.dart';\n\n@collection\nclass ModelA {\n  Id? id;\n}\n\n@collection\nclass ModelB {\n  Id? id;\n}\n\nvoid main() {\n  group('Clear', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelASchema, ModelBSchema]);\n    });\n\n    isarTest('Clear should empty target collection', () async {\n      final modelAs = List.generate(100, (_) => ModelA());\n      final modelBs = List.generate(200, (_) => ModelB());\n\n      await isar.tWriteTxn(() async {\n        await Future.wait([\n          isar.modelAs.tPutAll(modelAs),\n          isar.modelBs.tPutAll(modelBs),\n        ]);\n      });\n\n      await isar.modelAs.verify(modelAs);\n      await isar.modelBs.verify(modelBs);\n\n      await isar.tWriteTxn(\n        () => isar.modelAs.tClear(),\n      );\n\n      await isar.modelAs.verify([]);\n      await isar.modelBs.verify(modelBs);\n    });\n\n    isarTest('Isar clear should clear every collection', () async {\n      final modelAs = List.generate(250, (_) => ModelA());\n      final modelBs = List.generate(500, (_) => ModelB());\n\n      await isar.tWriteTxn(() async {\n        await Future.wait([\n          isar.modelAs.tPutAll(modelAs),\n          isar.modelBs.tPutAll(modelBs),\n        ]);\n      });\n\n      await isar.modelAs.verify(modelAs);\n      await isar.modelBs.verify(modelBs);\n\n      await isar.tWriteTxn(() => isar.tClear());\n\n      await isar.modelAs.verify([]);\n      await isar.modelBs.verify([]);\n    });\n\n    isarTest('Clear already cleared collection', () async {\n      await isar.tWriteTxn(\n        () => isar.modelAs.tPutAll(List.generate(42, (_) => ModelA())),\n      );\n      final count1 = await isar.modelAs.where().tCount();\n      expect(count1, 42);\n\n      await isar.tWriteTxn(() => isar.modelAs.tClear());\n      final count2 = await isar.modelAs.where().tCount();\n      expect(count2, 0);\n\n      await isar.tWriteTxn(() => isar.modelAs.tClear());\n      await isar.tWriteTxn(() => isar.modelAs.tClear());\n      await isar.tWriteTxn(() => isar.modelAs.tClear());\n\n      final count3 = await isar.modelAs.where().tCount();\n      expect(count3, 0);\n    });\n\n    isarTest('Clear should reset auto increment', () async {\n      final ids = await isar.tWriteTxn(\n        () => isar.modelAs.tPutAll(List.generate(20, (_) => ModelA())),\n      );\n\n      await isar.tWriteTxn(() => isar.modelAs.tClear());\n\n      final newIds = await isar.tWriteTxn(\n        () => isar.modelAs.tPutAll(List.generate(20, (_) => ModelA())),\n      );\n      expect(newIds, ids);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/collection_size_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'collection_size_test.g.dart';\n\n@collection\nclass ModelA {\n  ModelA({\n    required this.name,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index()\n  String name;\n\n  final bObj = IsarLink<ModelB>();\n  final bObjs = IsarLinks<ModelB>();\n\n  // We store a big buffer in order to better detect collection size changes,\n  // since reported collection size is based on block size (eg. 4096, 8192, ...)\n  final List<int> randomBuffer = List.filled(64000, 42);\n}\n\n@collection\nclass ModelB {\n  Id id = Isar.autoIncrement;\n\n  final List<int> randomBuffer = List.filled(64000, 42);\n}\n\nvoid main() {\n  group('Collection size', () {\n    late Isar isar;\n\n    late ModelA objA0;\n    late ModelA objA1;\n    late ModelA objA2;\n    late ModelA objA3;\n    late ModelA objA4;\n    late ModelA objA5;\n\n    late ModelB objB0;\n    late ModelB objB1;\n    late ModelB objB2;\n    late ModelB objB3;\n    late ModelB objB4;\n    late ModelB objB5;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelASchema, ModelBSchema]);\n\n      objA0 = ModelA(name: 'Obj A0');\n      objA1 = ModelA(name: 'Obj A1');\n      objA2 = ModelA(name: 'Obj A2');\n      objA3 = ModelA(name: 'Obj A3');\n      objA4 = ModelA(name: 'Obj A4');\n      objA5 = ModelA(name: 'Obj A5');\n\n      objB0 = ModelB();\n      objB1 = ModelB();\n      objB2 = ModelB();\n      objB3 = ModelB();\n      objB4 = ModelB();\n      objB5 = ModelB();\n    });\n\n    isarTest('Size should start at 0', () async {\n      expect(await isar.modelAs.tGetSize(), 0);\n      expect(await isar.modelAs.tGetSize(), 0);\n\n      expect(await isar.modelAs.tGetSize(includeIndexes: true), 0);\n      expect(await isar.modelAs.tGetSize(includeIndexes: true), 0);\n\n      expect(await isar.modelAs.tGetSize(includeLinks: true), 0);\n      expect(await isar.modelAs.tGetSize(includeLinks: true), 0);\n\n      expect(\n        await isar.modelAs.tGetSize(includeIndexes: true, includeLinks: true),\n        0,\n      );\n      expect(\n        await isar.modelAs.tGetSize(includeIndexes: true, includeLinks: true),\n        0,\n      );\n    });\n\n    isarTest('Size should increase with more entries', () async {\n      await isar.tWriteTxn(() => isar.modelAs.tPut(objA0));\n      final sizeA0 = await isar.modelAs.tGetSize();\n      expect(sizeA0, greaterThan(0));\n\n      await isar.tWriteTxn(() => isar.modelAs.tPutAll([objA1, objA2, objA3]));\n      final sizeA1 = await isar.modelAs.tGetSize();\n      expect(sizeA1, greaterThan(sizeA0));\n\n      await isar.tWriteTxn(() => isar.modelAs.tPutAll([objA4, objA5]));\n      final sizeA2 = await isar.modelAs.tGetSize();\n      expect(sizeA2, greaterThan(sizeA1));\n\n      expect(await isar.modelBs.tGetSize(), 0);\n\n      await isar.tWriteTxn(() => isar.modelBs.tPut(objB0));\n      final sizeB0 = await isar.modelBs.tGetSize();\n      expect(sizeB0, greaterThan(0));\n\n      await isar.tWriteTxn(() => isar.modelBs.tPutAll([objB1, objB2, objB3]));\n      final sizeB1 = await isar.modelBs.tGetSize();\n      expect(sizeB1, greaterThanOrEqualTo(sizeB0));\n\n      await isar.tWriteTxn(() => isar.modelBs.tPutAll([objB4, objB5]));\n      final sizeB2 = await isar.modelBs.tGetSize();\n      expect(sizeB2, greaterThanOrEqualTo(sizeB1));\n    });\n\n    isarTest('includeIndexes should change size', () async {\n      await isar.tWriteTxn(() => isar.modelAs.tPutAll([objA0, objA1, objA3]));\n\n      final sizeAWithoutIndexes = await isar.modelAs.tGetSize();\n      final sizeAWithIndexes =\n          await isar.modelAs.tGetSize(includeIndexes: true);\n      expect(sizeAWithIndexes, greaterThan(sizeAWithoutIndexes));\n\n      await isar.tWriteTxn(() => isar.modelBs.tPutAll([objB0, objB3, objB4]));\n\n      final sizeBWithoutIndexes = await isar.modelBs.tGetSize();\n      final sizeBWithIndexes =\n          await isar.modelBs.tGetSize(includeIndexes: true);\n      // ModelB has no indexes, so should stay the same\n      expect(sizeBWithIndexes, sizeBWithoutIndexes);\n    });\n\n    isarTest('includeLinks should change size', () async {\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.modelAs.tPutAll([objA0, objA1, objA2, objA3]),\n          isar.modelBs.tPutAll([objB0, objB1, objB2, objB3, objB4]),\n        ]),\n      );\n\n      objA1.bObj.value = objB0;\n      objA2.bObjs.addAll([objB0, objB1, objB4]);\n      objA3.bObj.value = objB0;\n      objA3.bObjs.addAll([objB0, objB1, objB3, objB4]);\n\n      final size1 = await isar.modelAs.tGetSize();\n      final size2 = await isar.modelAs.tGetSize(includeLinks: true);\n      expect(size1, size2);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          objA1.bObj.tSave(),\n          objA2.bObjs.tSave(),\n          objA3.bObj.tSave(),\n          objA3.bObjs.tSave(),\n        ]),\n      );\n\n      final size3 = await isar.modelAs.tGetSize();\n      final size4 = await isar.modelAs.tGetSize(includeLinks: true);\n      expect(size3, lessThan(size4));\n    });\n\n    isarTest('includeIndexes and includeLinks should change size', () async {\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.modelAs.tPutAll([objA0, objA1, objA2, objA3]),\n          isar.modelBs.tPutAll([objB0, objB1, objB2, objB3, objB4]),\n        ]),\n      );\n\n      objA1.bObj.value = objB0;\n      objA2.bObjs.addAll([objB0, objB1, objB4]);\n      objA3.bObj.value = objB0;\n      objA3.bObjs.addAll([objB0, objB1, objB3, objB4]);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          objA1.bObj.tSave(),\n          objA2.bObjs.tSave(),\n          objA3.bObj.tSave(),\n          objA3.bObjs.tSave(),\n        ]),\n      );\n\n      final size = await isar.modelAs.tGetSize();\n      final sizeWithIndexes = await isar.modelAs.tGetSize(includeIndexes: true);\n      final sizeWithLinks = await isar.modelAs.tGetSize(includeLinks: true);\n      final sizeWithIndexesAndLinks = await isar.modelAs.tGetSize(\n        includeIndexes: true,\n        includeLinks: true,\n      );\n      expect(size, lessThan(sizeWithIndexes));\n      expect(size, lessThan(sizeWithLinks));\n      expect(sizeWithIndexes, lessThan(sizeWithIndexesAndLinks));\n      expect(sizeWithLinks, lessThan(sizeWithIndexesAndLinks));\n    });\n\n    isarTest('Delete should decrease size', () async {\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.modelAs.tPutAll([objA0, objA1, objA2, objA3]),\n          isar.modelBs.tPutAll([objB0, objB1, objB2, objB3, objB4]),\n        ]),\n      );\n\n      final sizeA1 = await isar.modelAs.tGetSize();\n\n      await isar.tWriteTxn(() => isar.modelAs.tDelete(objA0.id));\n      final sizeA2 = await isar.modelAs.tGetSize();\n\n      expect(sizeA2, lessThan(sizeA1));\n\n      await isar.tWriteTxn(() => isar.modelAs.tClear());\n      final sizeA3 = await isar.modelAs.tGetSize();\n      expect(sizeA3, 0);\n\n      final sizeB1 = await isar.modelBs.tGetSize();\n\n      await isar.tWriteTxn(() => isar.modelBs.tDeleteAll([objB0.id, objB1.id]));\n      final sizeB2 = await isar.modelBs.tGetSize();\n\n      expect(sizeB2, lessThan(sizeB1));\n\n      await isar.tWriteTxn(() => isar.modelBs.tDeleteAll([objB2.id, objB3.id]));\n      final sizeB3 = await isar.modelBs.tGetSize();\n\n      expect(sizeB3, lessThan(sizeB2));\n      expect(sizeB3, greaterThan(0));\n    });\n\n    isarTest('Update should change size', () async {\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.modelAs.tPutAll([objA0, objA1, objA2, objA3]),\n          isar.modelBs.tPutAll([objB0, objB1, objB2, objB3, objB4]),\n        ]),\n      );\n\n      final sizeA1 = await isar.modelAs.tGetSize();\n      final sizeB1 = await isar.modelBs.tGetSize();\n\n      objA0.name += String.fromCharCodes(List.filled(64000, 42));\n\n      await isar.tWriteTxn(() => isar.modelAs.tPut(objA0));\n      final sizeA2 = await isar.modelAs.tGetSize();\n      final sizeB2 = await isar.modelBs.tGetSize();\n\n      expect(sizeA2, greaterThan(sizeA1));\n      expect(sizeB2, sizeB1);\n\n      objA0.name += String.fromCharCodes(List.filled(64000, 42));\n      objA1.name += String.fromCharCodes(List.filled(64000, 42));\n\n      await isar.tWriteTxn(() => isar.modelAs.tPutAll([objA0, objA1]));\n      final sizeA3 = await isar.modelAs.tGetSize();\n      final sizeB3 = await isar.modelBs.tGetSize();\n\n      expect(sizeA3, greaterThan(sizeA2));\n      expect(sizeB3, sizeB2);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/compact_on_launch_test.dart",
    "content": "import 'dart:io';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'compact_on_launch_test.g.dart';\n\n@Collection()\nclass Model {\n  Id id = Isar.autoIncrement;\n\n  List<int> buffer = List.filled(16000, 42);\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(buffer, other.buffer);\n\n  @override\n  String toString() {\n    return 'Model{id: $id}';\n  }\n}\n\nvoid main() {\n  group('Compact on launch', () {\n    late Isar isar;\n    late File file;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n      file = File(isar.path!);\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll(List.filled(100, Model())),\n      );\n    });\n\n    isarTestVm('No compact on launch', () async {\n      await isar.close();\n      final size1 = file.lengthSync();\n\n      isar = await openTempIsar([ModelSchema], name: isar.name);\n      await isar.tWriteTxn(() => isar.models.where().limit(50).tDeleteAll());\n      await isar.close();\n\n      final size2 = file.lengthSync();\n\n      isar = await openTempIsar([ModelSchema], name: isar.name);\n\n      expect(size1, size2);\n    });\n\n    isarTestVm('minFileSize', () async {\n      await isar.close();\n      final size1 = file.lengthSync();\n\n      isar = await openTempIsar([ModelSchema], name: isar.name);\n      await isar.tWriteTxn(() => isar.models.where().limit(50).tDeleteAll());\n      await isar.close();\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: CompactCondition(minFileSize: size1 * 2),\n      );\n      await isar.close();\n      final size2 = file.lengthSync();\n      expect(size1, size2);\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: CompactCondition(minFileSize: size1 ~/ 2),\n      );\n      final size3 = file.lengthSync();\n      expect(size3, lessThan(size2));\n    });\n\n    isarTestVm('minBytes', () async {\n      await isar.close();\n      final size1 = file.lengthSync();\n\n      isar = await openTempIsar([ModelSchema], name: isar.name);\n      await isar.tWriteTxn(() => isar.models.where().limit(10).tDeleteAll());\n      await isar.close();\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: CompactCondition(minBytes: size1 ~/ 2),\n      );\n      await isar.close();\n      final size2 = file.lengthSync();\n      expect(size1, size2);\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: CompactCondition(minBytes: size1 ~/ 2),\n      );\n      await isar.tWriteTxn(() => isar.models.where().limit(80).tDeleteAll());\n      await isar.close();\n      final size3 = file.lengthSync();\n      expect(size3, size2);\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: CompactCondition(minBytes: size1 ~/ 2),\n      );\n      final size4 = file.lengthSync();\n      expect(size4, lessThan(size3));\n    });\n\n    isarTestVm('minRatio', () async {\n      await isar.close();\n      final size1 = file.lengthSync();\n\n      isar = await openTempIsar([ModelSchema], name: isar.name);\n      await isar.tWriteTxn(() => isar.models.where().limit(10).tDeleteAll());\n      await isar.close();\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: const CompactCondition(minRatio: 2),\n      );\n      await isar.close();\n      final size2 = file.lengthSync();\n      expect(size1, size2);\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: const CompactCondition(minRatio: 2),\n      );\n      await isar.tWriteTxn(() => isar.models.where().limit(80).tDeleteAll());\n      await isar.close();\n      final size3 = file.lengthSync();\n      expect(size3, size2);\n\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isar.name,\n        compactOnLaunch: const CompactCondition(minRatio: 2),\n      );\n      final size4 = file.lengthSync();\n      expect(size4, lessThan(size3));\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/constructor_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'constructor_test.g.dart';\n\n@collection\nclass EmptyConstructorModel {\n  EmptyConstructorModel();\n  Id? id;\n\n  late String name;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is EmptyConstructorModel &&\n        other.id == id &&\n        other.name == name;\n  }\n}\n\n@collection\nclass NamedConstructorModel {\n  NamedConstructorModel({required this.name});\n  Id? id;\n\n  final String name;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is NamedConstructorModel &&\n        other.id == id &&\n        other.name == name;\n  }\n}\n\n@collection\nclass PositionalConstructorModel {\n  PositionalConstructorModel(this.id, this.name);\n  final Id? id;\n\n  final String name;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is PositionalConstructorModel &&\n        other.id == id &&\n        other.name == name;\n  }\n}\n\n@collection\nclass OptionalConstructorModel {\n  OptionalConstructorModel(this.name, [this.id]);\n  final Id? id;\n\n  final String name;\n\n  @override\n  String toString() => '{id: $id, name: $name}';\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is OptionalConstructorModel &&\n        other.id == id &&\n        other.name == name;\n  }\n}\n\n@collection\nclass PositionalNamedConstructorModel {\n  PositionalNamedConstructorModel(this.name, {required this.id});\n  final Id id;\n\n  String name;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is PositionalNamedConstructorModel &&\n        other.id == id &&\n        other.name == name;\n  }\n}\n\n@collection\nclass SerializeOnlyModel {\n  SerializeOnlyModel(this.id);\n  final Id? id;\n\n  final String name = 'myName';\n\n  String get someGetter => '$name$name';\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is SerializeOnlyModel && other.id == id;\n  }\n}\n\nvoid main() {\n  group('Constructor', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([\n        EmptyConstructorModelSchema,\n        NamedConstructorModelSchema,\n        PositionalConstructorModelSchema,\n        OptionalConstructorModelSchema,\n        PositionalNamedConstructorModelSchema,\n        SerializeOnlyModelSchema,\n      ]);\n    });\n\n    isarTest('EmptyConstructorModel', () async {\n      final obj1 = EmptyConstructorModel()..name = 'obj1';\n      final obj2 = EmptyConstructorModel()..name = 'obj2';\n      await isar.tWriteTxn(() async {\n        await isar.emptyConstructorModels.tPutAll([obj1, obj2]);\n      });\n\n      await qEqual(\n        isar.emptyConstructorModels.where(),\n        [obj1, obj2],\n      );\n    });\n\n    isarTest('NamedConstructorModel', () async {\n      final obj1 = NamedConstructorModel(name: 'obj1');\n      final obj2 = NamedConstructorModel(name: 'obj2');\n      await isar.tWriteTxn(() async {\n        await isar.namedConstructorModels.tPutAll([obj1, obj2]);\n      });\n\n      await qEqual(\n        isar.namedConstructorModels.where(),\n        [obj1, obj2],\n      );\n    });\n\n    isarTest('PositionalConstructorModel', () async {\n      final obj1 = PositionalConstructorModel(0, 'obj1');\n      final obj2 = PositionalConstructorModel(5, 'obj2');\n      final obj3 = PositionalConstructorModel(15, 'obj3');\n      await isar.tWriteTxn(() async {\n        await isar.positionalConstructorModels.tPutAll([obj1, obj2, obj3]);\n      });\n\n      await qEqual(\n        isar.positionalConstructorModels.where(),\n        [obj1, obj2, obj3],\n      );\n    });\n\n    isarTest('OptionalConstructorModel', () async {\n      final obj1 = OptionalConstructorModel('obj1');\n      final obj1WithId = OptionalConstructorModel('obj1', 1);\n      final obj2 = OptionalConstructorModel('obj2', 5);\n      final obj3 = OptionalConstructorModel('obj3', 15);\n      await isar.tWriteTxn(() async {\n        await isar.optionalConstructorModels.tPutAll([obj1, obj2, obj3]);\n      });\n\n      await qEqual(\n        isar.optionalConstructorModels.where(),\n        [obj1WithId, obj2, obj3],\n      );\n    });\n\n    isarTest('PositionalNamedConstructorModel', () async {\n      final obj1 = PositionalNamedConstructorModel('obj1', id: 1);\n      final obj2 = PositionalNamedConstructorModel('obj2', id: 2);\n      await isar.tWriteTxn(() async {\n        await isar.positionalNamedConstructorModels.tPutAll([obj1, obj2]);\n      });\n\n      await qEqual(\n        isar.positionalNamedConstructorModels.where(),\n        [obj1, obj2],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/copy_to_file_test.dart",
    "content": "import 'dart:io';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:test/test.dart';\n\npart 'copy_to_file_test.g.dart';\n\n@collection\nclass Model {\n  Id id = Isar.autoIncrement;\n\n  List<int> buffer = List.filled(16000, 42);\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(buffer, other.buffer);\n\n  @override\n  String toString() {\n    return 'Model{id: $id}';\n  }\n}\n\nvoid main() {\n  group('Copy to file', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema], maxSizeMiB: 20);\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll(List.filled(100, Model())),\n      );\n    });\n\n    isarTestVm('.copyToFile() should create a new file', () async {\n      final copiedDbFile = File(path.join(isar.directory!, getRandomName()));\n      expect(copiedDbFile.existsSync(), false);\n\n      await isar.copyToFile(copiedDbFile.path);\n\n      expect(copiedDbFile.existsSync(), true);\n      expect(copiedDbFile.lengthSync(), greaterThan(0));\n      await copiedDbFile.delete();\n    });\n\n    isarTestVm('.copyToFile() should keep the same content', () async {\n      final copiedDbFilename = getRandomName();\n      final copiedDbFile = File(\n        path.join(isar.directory!, '$copiedDbFilename.isar'),\n      );\n\n      await isar.copyToFile(copiedDbFile.path);\n\n      final copiedIsar = await openTempIsar(\n        [ModelSchema],\n        directory: isar.directory,\n        name: copiedDbFilename,\n        maxSizeMiB: 20,\n      );\n\n      final originalObjs = await isar.models.where().tFindAll();\n      await qEqual(\n        copiedIsar.models.where(),\n        originalObjs,\n      );\n    });\n\n    isarTestVm('.copyToFile() should compact copied file', () async {\n      await isar.tWriteTxn(() => isar.models.where().limit(50).tDeleteAll());\n\n      final copiedDbFilename1 = getRandomName();\n      final copiedDbFile1 = File(\n        path.join(\n          isar.directory!,\n          '$copiedDbFilename1.isar',\n        ),\n      );\n\n      await isar.copyToFile(copiedDbFile1.path);\n\n      final isarCopy1 = await openTempIsar(\n        [ModelSchema],\n        directory: isar.directory,\n        name: copiedDbFilename1,\n        maxSizeMiB: 20,\n      );\n\n      expect(copiedDbFile1.lengthSync(), greaterThan(0));\n      expect(\n        copiedDbFile1.lengthSync(),\n        lessThan(File(isar.path!).lengthSync()),\n      );\n\n      await isarCopy1.tWriteTxn(\n        () => isarCopy1.models.where().limit(25).tDeleteAll(),\n      );\n\n      final copiedDbFilename2 = getRandomName();\n      final copiedDbFile2 = File(\n        path.join(\n          isar.directory!,\n          '$copiedDbFilename2.isar',\n        ),\n      );\n      await isarCopy1.copyToFile(copiedDbFile2.path);\n\n      expect(copiedDbFile2.lengthSync(), greaterThan(0));\n      expect(\n        copiedDbFile2.lengthSync(),\n        lessThan(copiedDbFile1.lengthSync()),\n      );\n      await copiedDbFile2.delete();\n    });\n\n    isarTestVm('Copies should be the same size', () async {\n      final copiedDbFilename1 = getRandomName();\n      final copiedDbFile1 = File(\n        path.join(\n          isar.directory!,\n          '$copiedDbFilename1.isar',\n        ),\n      );\n\n      final copiedDbFilename2 = getRandomName();\n      final copiedDbFile2 = File(\n        path.join(\n          isar.directory!,\n          '$copiedDbFilename2.isar',\n        ),\n      );\n\n      await isar.copyToFile(copiedDbFile1.path);\n      await isar.copyToFile(copiedDbFile2.path);\n\n      expect(copiedDbFile1.lengthSync(), copiedDbFile2.lengthSync());\n      await copiedDbFile2.delete();\n\n      final isarCopy = await openTempIsar(\n        [ModelSchema],\n        directory: isar.directory,\n        name: copiedDbFilename1,\n        maxSizeMiB: 20,\n      );\n\n      final copiedDbFilename3 = getRandomName();\n      final copiedDbFile3 = File(\n        path.join(\n          isar.directory!,\n          '$copiedDbFilename3.isar',\n        ),\n      );\n\n      await isarCopy.copyToFile(copiedDbFile3.path);\n\n      expect(copiedDbFile3.lengthSync(), copiedDbFile1.lengthSync());\n      await copiedDbFile3.delete();\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/crud_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport 'user_model.dart';\n\npart 'crud_test.g.dart';\n\n@collection\nclass Message {\n  Id? id;\n\n  @Index()\n  String? message;\n\n  @override\n  String toString() {\n    return '{id: $id, message: $message}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    if (other is Message) {\n      return other.message == message;\n    } else {\n      return false;\n    }\n  }\n}\n\nvoid main() {\n  group('CRUD', () {\n    late Isar isar;\n    late IsarCollection<Message> messages;\n    late IsarCollection<UserModel> users;\n\n    setUp(() async {\n      isar = await openTempIsar([MessageSchema, UserModelSchema]);\n      messages = isar.messages;\n      users = isar.userModels;\n    });\n\n    isarTest('get() / put() without id', () async {\n      final message1 = Message()\n        ..id = Isar.autoIncrement\n        ..message = 'This is a new message';\n      final message2 = Message()..message = 'This is another new message';\n\n      await isar.tWriteTxn(() async {\n        message1.id = await messages.tPut(message1);\n        message2.id = await messages.tPut(message2);\n      });\n\n      expect(message1.id, 1);\n      final newMessage1 = await messages.tGet(message1.id!);\n      expect(message1, newMessage1);\n\n      expect(message2.id, 2);\n      final newMessage2 = await messages.tGet(message2.id!);\n      expect(message2, newMessage2);\n    });\n\n    isarTest('get() / put() with id', () async {\n      final message1 = Message()\n        ..id = 5\n        ..message = 'This is a new message';\n      final message2 = Message()..message = 'This is another new message';\n\n      await isar.tWriteTxn(() async {\n        await messages.tPut(message1);\n        await messages.tPut(message2);\n      });\n\n      final newMessage1 = await messages.tGet(message1.id!);\n      expect(message1.id, 5);\n      expect(newMessage1, message1);\n\n      expect(message2.id, 6);\n      final newMessage2 = await messages.tGet(message2.id!);\n      expect(newMessage2, message2);\n\n      final noMessage = await messages.tGet(7);\n      expect(noMessage, null);\n    });\n\n    isarTest('getAll() / putAll()', () async {\n      final message1 = Message()..message = 'Message one';\n      final message2 = Message()\n        ..message = 'Message two'\n        ..id = 9;\n      final message3 = Message()..message = 'Message three';\n\n      late List<Id> ids;\n      await isar.tWriteTxn(() async {\n        ids = await messages.tPutAll([message1, message2, message3]);\n      });\n\n      expect(ids, [1, 9, 10]);\n      final newMessages = await messages.tGetAll(ids);\n      expect(newMessages, [message1, message2, message3]);\n    });\n\n    isarTest('delete()', () async {\n      final user = UserModel()\n        ..name = 'Some User'\n        ..age = 24;\n\n      await isar.tWriteTxn(() async {\n        user.id = await users.tPut(user);\n      });\n\n      await isar.tWriteTxn(() async {\n        await users.tDelete(9999);\n      });\n      expect(await users.tGet(user.id!), user);\n\n      await isar.tWriteTxn(() async {\n        await users.tDelete(user.id!);\n      });\n      expect(await users.tGet(user.id!), null);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/default_value/common.dart",
    "content": "import 'package:isar/isar.dart';\n\npart 'common.g.dart';\n\nenum MyEnum {\n  value1,\n  value2,\n  value3;\n}\n\n@embedded\nclass MyEmbedded {\n  const MyEmbedded([this.test = '']);\n\n  final String test;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is MyEmbedded && other.test == test;\n}\n\n@Name('Col')\n@collection\nclass EmptyModel {\n  EmptyModel(this.id);\n\n  final Id id;\n}\n"
  },
  {
    "path": "packages/isar_test/test/default_value/default_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport 'common.dart';\n\npart 'default_test.g.dart';\n\n@Name('Col')\n@collection\nclass DefaultModel {\n  DefaultModel(\n    this.id, [\n    this.boolValue = true,\n    this.byteValue = 55,\n    this.shortValue = 123,\n    this.intValue = 1234,\n    this.floatValue = 5.2,\n    this.doubleValue = 10.10,\n    this.stringValue = 'hello',\n    this.embeddedValue = const MyEmbedded('abc'),\n  ]);\n\n  final Id id;\n\n  final bool boolValue;\n\n  final byte byteValue;\n\n  final short shortValue;\n\n  final int intValue;\n\n  final float floatValue;\n\n  final double doubleValue;\n\n  final String stringValue;\n\n  final MyEmbedded embeddedValue;\n}\n\n@Name('Col')\n@collection\nclass DefaultListModel {\n  DefaultListModel(\n    this.id, [\n    this.boolValue = const [true, false],\n    this.byteValue = const [1, 3],\n    this.shortValue = const [null, 23, 34],\n    this.intValue = const [123, 234, null],\n    this.floatValue = const [5.5, null],\n    this.doubleValue = const [null, 10.10],\n    this.stringValue = const ['abc', null, 'def'],\n    this.embeddedValue = const [null, MyEmbedded('test')],\n  ]);\n\n  final Id id;\n\n  final List<bool?> boolValue;\n\n  final List<byte> byteValue;\n\n  final List<short?> shortValue;\n\n  final List<int?> intValue;\n\n  final List<float?> floatValue;\n\n  final List<double?> doubleValue;\n\n  final List<String?> stringValue;\n\n  final List<MyEmbedded?> embeddedValue;\n}\n\nvoid main() {\n  group('Default value', () {\n    isarTest('scalar', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 = await openTempIsar([DefaultModelSchema], name: isarName);\n      final obj = (await isar2.defaultModels.tGet(0))!;\n      expect(obj.boolValue, true);\n      expect(obj.byteValue, 55);\n      expect(obj.shortValue, 123);\n      expect(obj.intValue, 1234);\n      expect(obj.floatValue, 5.2);\n      expect(obj.doubleValue, 10.10);\n      expect(obj.stringValue, 'hello');\n      expect(obj.embeddedValue, const MyEmbedded('abc'));\n    });\n\n    isarTest('scalar property', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 = await openTempIsar([DefaultModelSchema], name: isarName);\n      expect(\n        await isar2.defaultModels.where().boolValueProperty().tFindFirst(),\n        true,\n      );\n      expect(\n        await isar2.defaultModels.where().byteValueProperty().tFindFirst(),\n        55,\n      );\n      expect(\n        await isar2.defaultModels.where().shortValueProperty().tFindFirst(),\n        123,\n      );\n      expect(\n        await isar2.defaultModels.where().intValueProperty().tFindFirst(),\n        1234,\n      );\n      expect(\n        await isar2.defaultModels.where().floatValueProperty().tFindFirst(),\n        5.2,\n      );\n      expect(\n        await isar2.defaultModels.where().doubleValueProperty().tFindFirst(),\n        10.10,\n      );\n      expect(\n        await isar2.defaultModels.where().stringValueProperty().tFindFirst(),\n        'hello',\n      );\n      expect(\n        await isar2.defaultModels.where().embeddedValueProperty().tFindFirst(),\n        const MyEmbedded('abc'),\n      );\n    });\n\n    isarTest('list', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 =\n          await openTempIsar([DefaultListModelSchema], name: isarName);\n      final obj = (await isar2.defaultListModels.tGet(0))!;\n      expect(obj.boolValue, [true, false]);\n      expect(obj.byteValue, [1, 3]);\n      expect(obj.shortValue, [null, 23, 34]);\n      expect(obj.intValue, [123, 234, null]);\n      expect(obj.floatValue, [5.5, null]);\n      expect(obj.doubleValue, [null, 10.10]);\n      expect(obj.stringValue, ['abc', null, 'def']);\n      expect(obj.embeddedValue, [null, const MyEmbedded('test')]);\n    });\n\n    isarTest('list property', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 =\n          await openTempIsar([DefaultListModelSchema], name: isarName);\n      expect(\n        await isar2.defaultListModels.where().boolValueProperty().tFindFirst(),\n        [true, false],\n      );\n      expect(\n        await isar2.defaultListModels.where().byteValueProperty().tFindFirst(),\n        [1, 3],\n      );\n      expect(\n        await isar2.defaultListModels.where().shortValueProperty().tFindFirst(),\n        [null, 23, 34],\n      );\n      expect(\n        await isar2.defaultListModels.where().intValueProperty().tFindFirst(),\n        [123, 234, null],\n      );\n      expect(\n        await isar2.defaultListModels.where().floatValueProperty().tFindFirst(),\n        [5.5, null],\n      );\n      expect(\n        await isar2.defaultListModels\n            .where()\n            .doubleValueProperty()\n            .tFindFirst(),\n        [null, 10.10],\n      );\n      expect(\n        await isar2.defaultListModels\n            .where()\n            .stringValueProperty()\n            .tFindFirst(),\n        ['abc', null, 'def'],\n      );\n      expect(\n        await isar2.defaultListModels\n            .where()\n            .embeddedValueProperty()\n            .tFindFirst(),\n        [null, const MyEmbedded('test')],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/default_value/no_default_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport 'common.dart';\n\npart 'no_default_test.g.dart';\n\n@Name('Col')\n@collection\nclass NoDefaultModel {\n  NoDefaultModel(\n    this.id,\n    this.boolValue,\n    this.byteValue,\n    this.shortValue,\n    this.intValue,\n    this.floatValue,\n    this.doubleValue,\n    this.dateTimeValue,\n    this.stringValue,\n    this.embeddedValue,\n    this.enumValue,\n  );\n\n  final Id id;\n\n  final bool boolValue;\n\n  final byte byteValue;\n\n  final short shortValue;\n\n  final int intValue;\n\n  final float floatValue;\n\n  final double doubleValue;\n\n  final DateTime dateTimeValue;\n\n  final String stringValue;\n\n  final MyEmbedded embeddedValue;\n\n  @Enumerated(EnumType.name)\n  final MyEnum enumValue;\n}\n\n@Name('Col')\n@collection\nclass NoDefaultListModel {\n  NoDefaultListModel(\n    this.id,\n    this.boolValue,\n    this.byteValue,\n    this.shortValue,\n    this.intValue,\n    this.floatValue,\n    this.doubleValue,\n    this.dateTimeValue,\n    this.stringValue,\n    this.embeddedValue,\n    this.enumValue,\n  );\n\n  final Id id;\n\n  final List<bool?> boolValue;\n\n  final List<byte> byteValue;\n\n  final List<short?> shortValue;\n\n  final List<int?> intValue;\n\n  final List<float?> floatValue;\n\n  final List<double?> doubleValue;\n\n  final List<DateTime?> dateTimeValue;\n\n  final List<String?> stringValue;\n\n  final List<MyEmbedded?> embeddedValue;\n\n  @Enumerated(EnumType.name)\n  final List<MyEnum?> enumValue;\n}\n\nvoid main() {\n  group('No default value', () {\n    isarTest('scalar', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 = await openTempIsar([NoDefaultModelSchema], name: isarName);\n      final obj = (await isar2.noDefaultModels.tGet(0))!;\n      expect(obj.boolValue, false);\n      expect(obj.byteValue, 0);\n      expect(obj.shortValue, -2147483648);\n      expect(obj.intValue, -9223372036854775808);\n      expect(obj.floatValue, isNaN);\n      expect(obj.doubleValue, isNaN);\n      expect(\n        obj.dateTimeValue,\n        DateTime.fromMillisecondsSinceEpoch(0),\n      );\n      expect(obj.stringValue, '');\n      expect(obj.embeddedValue, const MyEmbedded());\n      expect(obj.enumValue, MyEnum.value1);\n    });\n\n    isarTest('scalar property', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 = await openTempIsar([NoDefaultModelSchema], name: isarName);\n      expect(\n        await isar2.noDefaultModels.where().boolValueProperty().tFindFirst(),\n        false,\n      );\n      expect(\n        await isar2.noDefaultModels.where().byteValueProperty().tFindFirst(),\n        0,\n      );\n      expect(\n        await isar2.noDefaultModels.where().shortValueProperty().tFindFirst(),\n        -2147483648,\n      );\n      expect(\n        await isar2.noDefaultModels.where().intValueProperty().tFindFirst(),\n        -9223372036854775808,\n      );\n      expect(\n        await isar2.noDefaultModels.where().floatValueProperty().tFindFirst(),\n        isNaN,\n      );\n      expect(\n        await isar2.noDefaultModels.where().doubleValueProperty().tFindFirst(),\n        isNaN,\n      );\n      expect(\n        await isar2.noDefaultModels\n            .where()\n            .dateTimeValueProperty()\n            .tFindFirst(),\n        DateTime.fromMillisecondsSinceEpoch(0),\n      );\n      expect(\n        await isar2.noDefaultModels.where().stringValueProperty().tFindFirst(),\n        '',\n      );\n      expect(\n        await isar2.noDefaultModels\n            .where()\n            .embeddedValueProperty()\n            .tFindFirst(),\n        const MyEmbedded(),\n      );\n      expect(\n        await isar2.noDefaultModels.where().enumValueProperty().tFindFirst(),\n        MyEnum.value1,\n      );\n    });\n\n    isarTest('list', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 =\n          await openTempIsar([NoDefaultListModelSchema], name: isarName);\n      final obj = (await isar2.noDefaultListModels.tGet(0))!;\n      expect(obj.boolValue, isEmpty);\n      expect(obj.byteValue, isEmpty);\n      expect(obj.shortValue, isEmpty);\n      expect(obj.intValue, isEmpty);\n      expect(obj.floatValue, isEmpty);\n      expect(obj.doubleValue, isEmpty);\n      expect(obj.dateTimeValue, isEmpty);\n      expect(obj.stringValue, isEmpty);\n      expect(obj.embeddedValue, isEmpty);\n      expect(obj.enumValue, isEmpty);\n    });\n\n    isarTest('list property', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 =\n          await openTempIsar([NoDefaultListModelSchema], name: isarName);\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .boolValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .byteValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .shortValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels.where().intValueProperty().tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .floatValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .doubleValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .dateTimeValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .stringValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .embeddedValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n      expect(\n        await isar2.noDefaultListModels\n            .where()\n            .enumValueProperty()\n            .tFindFirst(),\n        isEmpty,\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/default_value/nullable_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport 'common.dart';\n\npart 'nullable_test.g.dart';\n\n@Name('Col')\n@collection\nclass NullableModel {\n  NullableModel(\n    this.id,\n    this.boolValue,\n    this.shortValue,\n    this.intValue,\n    this.floatValue,\n    this.doubleValue,\n    this.dateTimeValue,\n    this.stringValue,\n    this.embeddedValue,\n    this.enumValue,\n  );\n\n  final Id id;\n\n  final bool? boolValue;\n\n  final short? shortValue;\n\n  final int? intValue;\n\n  final float? floatValue;\n\n  final double? doubleValue;\n\n  final DateTime? dateTimeValue;\n\n  final String? stringValue;\n\n  final MyEmbedded? embeddedValue;\n\n  @Enumerated(EnumType.name)\n  final MyEnum? enumValue;\n}\n\n@Name('Col')\n@collection\nclass NullableListModel {\n  NullableListModel(\n    this.id,\n    this.boolValue,\n    this.byteValue,\n    this.shortValue,\n    this.intValue,\n    this.floatValue,\n    this.doubleValue,\n    this.dateTimeValue,\n    this.stringValue,\n    this.embeddedValue,\n    this.enumValue,\n  );\n\n  final Id id;\n\n  final List<bool>? boolValue;\n\n  final List<byte>? byteValue;\n\n  final List<short>? shortValue;\n\n  final List<int>? intValue;\n\n  final List<float>? floatValue;\n\n  final List<double>? doubleValue;\n\n  final List<DateTime>? dateTimeValue;\n\n  final List<String>? stringValue;\n\n  final List<MyEmbedded>? embeddedValue;\n\n  @enumerated\n  final List<MyEnum>? enumValue;\n}\n\nvoid main() {\n  group('Nullable value', () {\n    isarTest('scalar', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 = await openTempIsar([NullableModelSchema], name: isarName);\n      final obj = (await isar2.nullableModels.tGet(0))!;\n      expect(obj.boolValue, null);\n      expect(obj.shortValue, null);\n      expect(obj.intValue, null);\n      expect(obj.floatValue, null);\n      expect(obj.doubleValue, null);\n      expect(obj.dateTimeValue, null);\n      expect(obj.stringValue, null);\n      expect(obj.embeddedValue, null);\n      expect(obj.enumValue, null);\n    });\n\n    isarTest('scalar property', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 = await openTempIsar([NullableModelSchema], name: isarName);\n      expect(\n        await isar2.nullableModels.where().boolValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().shortValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().intValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().floatValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().doubleValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().dateTimeValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().stringValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().embeddedValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableModels.where().enumValueProperty().tFindFirst(),\n        null,\n      );\n    });\n\n    isarTest('list', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 =\n          await openTempIsar([NullableListModelSchema], name: isarName);\n      final obj = (await isar2.nullableListModels.tGet(0))!;\n      expect(obj.boolValue, null);\n      expect(obj.shortValue, null);\n      expect(obj.intValue, null);\n      expect(obj.floatValue, null);\n      expect(obj.doubleValue, null);\n      expect(obj.dateTimeValue, null);\n      expect(obj.stringValue, null);\n      expect(obj.embeddedValue, null);\n      expect(obj.enumValue, null);\n    });\n\n    isarTest('list property', () async {\n      final emptyObj = EmptyModel(0);\n      final isar1 = await openTempIsar([EmptyModelSchema]);\n      await isar1.tWriteTxn(() => isar1.emptyModels.tPut(emptyObj));\n      final isarName = isar1.name;\n      await isar1.close();\n\n      final isar2 =\n          await openTempIsar([NullableListModelSchema], name: isarName);\n      expect(\n        await isar2.nullableListModels.where().boolValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels\n            .where()\n            .shortValueProperty()\n            .tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels.where().intValueProperty().tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels\n            .where()\n            .floatValueProperty()\n            .tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels\n            .where()\n            .doubleValueProperty()\n            .tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels\n            .where()\n            .dateTimeValueProperty()\n            .tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels\n            .where()\n            .stringValueProperty()\n            .tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels\n            .where()\n            .embeddedValueProperty()\n            .tFindFirst(),\n        null,\n      );\n      expect(\n        await isar2.nullableListModels.where().enumValueProperty().tFindFirst(),\n        null,\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/embedded_test.dart",
    "content": "// ignore_for_file: hash_and_equals\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'embedded_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.id, this.embedded, this.nested, this.nestedList);\n\n  final Id id;\n\n  final EModel? embedded;\n\n  final NModel? nested;\n\n  final List<NModel>? nestedList;\n\n  @override\n  bool operator ==(Object other) =>\n      other is Model &&\n      other.id == id &&\n      other.embedded == embedded &&\n      other.nested == nested &&\n      listEquals(other.nestedList, nestedList);\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'embedded': embedded?.toJson(),\n      'nested': nested?.toJson(),\n      'nestedList': nestedList?.map((e) => e.toJson()).toList(),\n    };\n  }\n}\n\n@embedded\nclass EModel {\n  EModel([this.value = '']);\n\n  final String value;\n\n  @override\n  bool operator ==(Object other) => other is EModel && other.value == value;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'value': value,\n    };\n  }\n}\n\n@embedded\nclass NModel {\n  NModel([this.embedded, this.nested, this.nestedList]);\n\n  final EModel? embedded;\n\n  final NModel? nested;\n\n  final List<NModel?>? nestedList;\n\n  @override\n  bool operator ==(Object other) =>\n      other is NModel &&\n      other.embedded == embedded &&\n      other.nested == nested &&\n      listEquals(other.nestedList, nestedList);\n\n  Map<String, dynamic> toJson() {\n    return {\n      'embedded': embedded?.toJson(),\n      'nested': nested?.toJson(),\n      'nestedList': nestedList?.map((e) => e?.toJson()).toList(),\n    };\n  }\n}\n\nvoid main() {\n  group('Embedded', () {\n    late Isar isar;\n\n    late Model allNull;\n    late Model simple;\n    late Model nested;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      allNull = Model(0, null, null, null);\n      simple = Model(\n        1,\n        EModel('hello'),\n        NModel(EModel('abc')),\n        [NModel(EModel('test'))],\n      );\n      nested = Model(\n        2,\n        EModel('hello'),\n        NModel(\n          EModel('abc'),\n          NModel(\n            EModel('this is level2'),\n            NModel(null, null, []),\n            [\n              NModel(\n                EModel('i am part of a list'),\n                NModel(\n                  EModel('even deeper'),\n                  NModel(null, null, []),\n                  [],\n                ),\n              ),\n              null,\n              NModel(\n                EModel('hello'),\n              )\n            ],\n          ),\n        ),\n        [\n          NModel(EModel('test')),\n          NModel(\n            EModel('i am also part of a list'),\n            NModel(\n              EModel('even deeper'),\n              NModel(null, NModel(null, NModel(null, null, []), []), []),\n              [],\n            ),\n            [null, null, null],\n          ),\n        ],\n      );\n    });\n\n    isarTest('.put() .get()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.models.tPutAll([allNull, simple, nested]);\n      });\n\n      await isar.models.verify([allNull, simple, nested]);\n\n      await qEqual(isar.models.where(), [allNull, simple, nested]);\n    });\n\n    isarTest('.importJson()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.models.tImportJson([\n          allNull.toJson(),\n          simple.toJson(),\n          nested.toJson(),\n        ]);\n      });\n\n      await isar.models.verify([allNull, simple, nested]);\n    });\n\n    isarTest('.exportJson()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.models.tPutAll([allNull, simple, nested]);\n      });\n\n      expect(\n        await isar.models.where().exportJson(),\n        [\n          allNull.toJson(),\n          simple.toJson(),\n          nested.toJson(),\n        ],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/enum_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'enum_test.g.dart';\n\nenum TestEnum {\n  option1(1, 1, 1, 'test1'),\n  option2(2, 2, 2, 'test2'),\n  option3(3, 3, 3, 'test3');\n\n  const TestEnum(\n    this.byteVal,\n    this.shortVal,\n    this.intVal,\n    this.stringVal,\n  );\n\n  final byte byteVal;\n  final short shortVal;\n  final int intVal;\n  final String stringVal;\n}\n\n@collection\nclass EnumModel {\n  EnumModel(\n    this.id,\n    this.ordinalEnum,\n    this.nameEnum,\n    this.byteEnum,\n    this.shortEnum,\n    this.intEnum,\n    this.stringEnum,\n  );\n\n  EnumModel.test(TestEnum value)\n      : id = Isar.autoIncrement,\n        ordinalEnum = value,\n        nameEnum = value,\n        byteEnum = value,\n        shortEnum = value,\n        intEnum = value,\n        stringEnum = value;\n\n  static final model1 = EnumModel.test(TestEnum.option1);\n  static final model2 = EnumModel.test(TestEnum.option2);\n  static final model3 = EnumModel.test(TestEnum.option3);\n\n  final Id id;\n\n  @enumerated\n  final TestEnum ordinalEnum;\n\n  @Enumerated(EnumType.name)\n  final TestEnum nameEnum;\n\n  @Enumerated(EnumType.value, 'byteVal')\n  final TestEnum byteEnum;\n\n  @Enumerated(EnumType.value, 'shortVal')\n  final TestEnum shortEnum;\n\n  @Enumerated(EnumType.value, 'intVal')\n  final TestEnum intEnum;\n\n  @Enumerated(EnumType.value, 'stringVal')\n  final TestEnum stringEnum;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is EnumModel &&\n      other.ordinalEnum == ordinalEnum &&\n      other.nameEnum == nameEnum &&\n      other.byteEnum == byteEnum &&\n      other.shortEnum == shortEnum &&\n      other.intEnum == intEnum &&\n      other.stringEnum == stringEnum;\n\n  @override\n  String toString() {\n    return '''EnumModel{ordinalEnum: $ordinalEnum, nameEnum: $nameEnum, byteEnum: $byteEnum, shortEnum: $shortEnum, intEnum: $intEnum, stringEnum: $stringEnum}''';\n  }\n}\n\nvoid main() {\n  group('Enum', () {\n    isarTest('Verify property types', () {});\n\n    isarTest('.get() / .put()', () async {\n      final isar = await openTempIsar([EnumModelSchema]);\n      await isar.tWriteTxn(() async {\n        await isar.enumModels\n            .tPutAll([EnumModel.model1, EnumModel.model2, EnumModel.model3]);\n      });\n\n      await qEqual(\n        isar.enumModels.where(),\n        [EnumModel.model1, EnumModel.model2, EnumModel.model3],\n      );\n    });\n\n    isarTest('DateTime Enum', () {});\n\n    isarTest('Added value', () {});\n\n    isarTest('Removed value', () {});\n\n    isarTest('.exportJson()', () {});\n\n    isarTest('.importJson()', () {});\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_bool_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_bool_list_test.g.dart';\n\n@collection\nclass BoolModel {\n  BoolModel(this.list);\n\n  Id? id;\n\n  @Index(type: IndexType.value)\n  List<bool?>? list;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is BoolModel && other.id == id && listEquals(list, other.list);\n  }\n}\n\nvoid main() {\n  group('Bool list filter', () {\n    late Isar isar;\n    late IsarCollection<BoolModel> col;\n\n    late BoolModel objEmpty;\n    late BoolModel obj1;\n    late BoolModel obj2;\n    late BoolModel obj3;\n    late BoolModel obj4;\n    late BoolModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([BoolModelSchema]);\n      col = isar.boolModels;\n\n      objEmpty = BoolModel([]);\n      obj1 = BoolModel([true]);\n      obj2 = BoolModel([null, false]);\n      obj3 = BoolModel([true, false, true]);\n      obj4 = BoolModel([null]);\n      objNull = BoolModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objEmpty, obj1, obj2, obj3, obj4, objNull]);\n      });\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqual(col.filter().listElementEqualTo(true), [obj1, obj3]);\n      await qEqual(col.filter().listElementEqualTo(null), [obj2, obj4]);\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqual(col.where().filter().listElementIsNull(), [obj2, obj4]);\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqual(\n        col.where().filter().listElementIsNotNull(),\n        [obj1, obj2, obj3],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.where().filter().listIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(\n        col.where().filter().listIsNotNull(),\n        [objEmpty, obj1, obj2, obj3, obj4],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_bool_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_bool_test.g.dart';\n\n@collection\nclass BoolModel {\n  BoolModel(this.field);\n\n  Id? id;\n\n  bool? field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is BoolModel && other.id == id && other.field == field;\n  }\n}\n\nvoid main() {\n  group('Bool filter', () {\n    late Isar isar;\n    late IsarCollection<BoolModel> col;\n\n    late BoolModel objNull;\n    late BoolModel objFalse;\n    late BoolModel objTrue;\n    late BoolModel objFalse2;\n\n    setUp(() async {\n      isar = await openTempIsar([BoolModelSchema]);\n      col = isar.boolModels;\n\n      objNull = BoolModel(null);\n      objFalse = BoolModel(false);\n      objTrue = BoolModel(true);\n      objFalse2 = BoolModel(false);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objNull, objFalse, objTrue, objFalse2]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.filter().fieldEqualTo(true), [objTrue]);\n      await qEqualSet(\n        col.filter().fieldEqualTo(false),\n        [objFalse, objFalse2],\n      );\n      await qEqual(col.filter().fieldEqualTo(null), [objNull]);\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqualSet(col.where().filter().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqualSet(\n        col.where().filter().fieldIsNotNull(),\n        [objFalse, objTrue, objFalse2],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_byte_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_byte_list_test.g.dart';\n\n@collection\nclass ByteModel {\n  ByteModel(this.list);\n\n  Id? id;\n\n  List<byte>? list;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is ByteModel && other.id == id && listEquals(list, other.list);\n  }\n}\n\nvoid main() {\n  group('Byte list filter', () {\n    late Isar isar;\n    late IsarCollection<ByteModel> col;\n\n    late ByteModel objEmpty;\n    late ByteModel obj1;\n    late ByteModel obj2;\n    late ByteModel obj3;\n    late ByteModel obj4;\n    late ByteModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([ByteModelSchema]);\n      col = isar.byteModels;\n\n      objEmpty = ByteModel([]);\n      obj1 = ByteModel([123]);\n      obj2 = ByteModel([0, 255]);\n      obj3 = ByteModel([1, 123, 3]);\n      obj4 = ByteModel([0, 255]);\n      objNull = ByteModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objEmpty, obj1, obj2, obj3, obj4, objNull]);\n      });\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqual(\n        col.filter().listElementEqualTo(0),\n        [obj2, obj4],\n      );\n      await qEqual(col.filter().listElementEqualTo(1), [obj3]);\n      await qEqual(col.filter().listElementEqualTo(55), []);\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqual(col.filter().listElementGreaterThan(123), [obj2, obj4]);\n      await qEqual(\n        col.filter().listElementGreaterThan(123, include: true),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(col.filter().listElementGreaterThan(255), []);\n    });\n\n    isarTest('.elementLessThan()', () async {\n      await qEqual(col.filter().listElementLessThan(123), [obj2, obj3, obj4]);\n      await qEqual(\n        col.filter().listElementLessThan(123, include: true),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(col.filter().listElementLessThan(0), []);\n    });\n\n    isarTest('.elementBetween()', () async {\n      await qEqual(\n        col.filter().listElementBetween(123, 255),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(\n        col.filter().listElementBetween(123, 255, includeLower: false),\n        [obj2, obj4],\n      );\n      await qEqual(\n        col.filter().listElementBetween(123, 255, includeUpper: false),\n        [obj1, obj3],\n      );\n      await qEqual(col.filter().listElementBetween(50, 100), []);\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.where().filter().listIsNull(), [objNull]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_byte_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_byte_test.g.dart';\n\n@collection\nclass ByteModel {\n  ByteModel(this.field);\n\n  Id? id;\n\n  byte field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is ByteModel && other.id == id && other.field == field;\n  }\n}\n\nvoid main() {\n  group('Byte filter', () {\n    late Isar isar;\n    late IsarCollection<ByteModel> col;\n\n    late ByteModel objMin;\n    late ByteModel obj1;\n    late ByteModel obj2;\n    late ByteModel obj3;\n    late ByteModel objMax;\n\n    setUp(() async {\n      isar = await openTempIsar([ByteModelSchema]);\n      col = isar.byteModels;\n\n      objMin = ByteModel(0);\n      obj1 = ByteModel(1);\n      obj2 = ByteModel(123);\n      obj3 = ByteModel(1);\n      objMax = ByteModel(255);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objMin, obj1, obj2, obj3, objMax]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.filter().fieldEqualTo(0), [objMin]);\n      await qEqual(col.filter().fieldEqualTo(1), [obj1, obj3]);\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(\n        col.filter().fieldGreaterThan(0),\n        [obj1, obj2, obj3, objMax],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(0, include: true),\n        [objMin, obj1, obj2, obj3, objMax],\n      );\n      await qEqual(col.filter().fieldGreaterThan(255), []);\n      await qEqual(\n        col.filter().fieldGreaterThan(255, include: true),\n        [objMax],\n      );\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.filter().fieldLessThan(255), [objMin, obj1, obj2, obj3]);\n      await qEqual(\n        col.filter().fieldLessThan(255, include: true),\n        [objMin, obj1, obj2, obj3, objMax],\n      );\n      await qEqual(col.filter().fieldLessThan(0), []);\n      await qEqual(col.filter().fieldLessThan(0, include: true), [objMin]);\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.filter().fieldBetween(0, 255),\n        [objMin, obj1, obj2, obj3, objMax],\n      );\n      await qEqual(\n        col.filter().fieldBetween(0, 255, includeLower: false),\n        [obj1, obj2, obj3, objMax],\n      );\n      await qEqual(\n        col.filter().fieldBetween(0, 255, includeUpper: false),\n        [objMin, obj1, obj2, obj3],\n      );\n      await qEqual(col.filter().fieldBetween(255, 0), []);\n      await qEqual(col.filter().fieldBetween(100, 110), []);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_date_time_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_date_time_list_test.g.dart';\n\n@collection\nclass DateTimeModel {\n  DateTimeModel(this.list);\n  Id? id;\n\n  List<DateTime?>? list;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is DateTimeModel &&\n      id == other.id &&\n      listEquals(\n        list?.map((e) => e?.toUtc()).toList(),\n        other.list?.map((e) => e?.toUtc()).toList(),\n      );\n}\n\nDateTime local(int year, [int month = 1, int day = 1]) {\n  return DateTime(year, month, day);\n}\n\nDateTime utc(int year, [int month = 1, int day = 1]) {\n  return local(year, month, day).toUtc();\n}\n\nvoid main() {\n  group('DateTime list filter', () {\n    late Isar isar;\n    late IsarCollection<DateTimeModel> col;\n\n    late DateTimeModel obj1;\n    late DateTimeModel obj2;\n    late DateTimeModel obj3;\n    late DateTimeModel obj4;\n    late DateTimeModel objEmpty;\n    late DateTimeModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([DateTimeModelSchema]);\n      col = isar.dateTimeModels;\n\n      obj1 = DateTimeModel([null]);\n      obj2 = DateTimeModel([local(2020), utc(2030), local(2020)]);\n      obj3 = DateTimeModel([local(2010), utc(2020)]);\n      obj4 = DateTimeModel([utc(2030), local(2050)]);\n      objEmpty = DateTimeModel([]);\n      objNull = DateTimeModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([obj2, obj4, obj3, objEmpty, obj1, objNull]);\n      });\n    });\n\n    group('DateTime list filter', () {\n      isarTest('.elementGreaterThan()', () async {\n        await qEqual(\n          col.filter().listElementGreaterThan(local(2020)),\n          [obj2, obj4],\n        );\n        await qEqual(\n          col.filter().listElementGreaterThan(utc(2020), include: true),\n          [obj2, obj4, obj3],\n        );\n        await qEqual(\n          col.filter().listElementGreaterThan(null),\n          [obj2, obj4, obj3],\n        );\n        await qEqual(\n          col.filter().listElementGreaterThan(null, include: true),\n          [obj2, obj4, obj3, obj1],\n        );\n      });\n\n      isarTest('.elementLessThan()', () async {\n        await qEqual(col.filter().listElementLessThan(utc(2020)), [obj3, obj1]);\n        await qEqual(\n          col.filter().listElementLessThan(local(2020), include: true),\n          [obj2, obj3, obj1],\n        );\n        await qEqual(col.filter().listElementLessThan(null), []);\n        await qEqual(\n          col.filter().listElementLessThan(null, include: true),\n          [obj1],\n        );\n      });\n\n      isarTest('.elementBetween()', () async {\n        await qEqual(\n          col.filter().listElementBetween(utc(2010), utc(2020)),\n          [obj2, obj3],\n        );\n        await qEqual(\n          col.filter().listElementBetween(\n                utc(2010),\n                utc(2020),\n                includeUpper: false,\n              ),\n          [obj3],\n        );\n        await qEqual(\n          col.filter().listElementBetween(null, utc(2010)),\n          [obj3, obj1],\n        );\n        await qEqual(\n          col.filter().listElementBetween(\n                null,\n                utc(2010),\n                includeLower: false,\n              ),\n          [obj3],\n        );\n        await qEqual(\n          col.filter().listElementBetween(\n                null,\n                utc(2010),\n                includeUpper: false,\n              ),\n          [obj1],\n        );\n      });\n\n      isarTest('.elementIsNull()', () async {\n        await qEqual(col.filter().listElementIsNull(), [obj1]);\n      });\n\n      isarTest('.elementIsNotNull()', () async {\n        await qEqual(col.filter().listElementIsNotNull(), [obj2, obj4, obj3]);\n      });\n\n      isarTest('.isNull()', () async {\n        await qEqual(col.filter().listIsNull(), [objNull]);\n      });\n\n      isarTest('.isNotNull()', () async {\n        await qEqual(\n          col.filter().listIsNotNull(),\n          [obj2, obj4, obj3, objEmpty, obj1],\n        );\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_date_time_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_date_time_test.g.dart';\n\n@collection\nclass DateTimeModel {\n  DateTimeModel(this.field);\n  Id? id;\n\n  DateTime? field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is DateTimeModel &&\n      id == other.id &&\n      other.field?.toUtc() == field?.toUtc();\n\n  @override\n  String toString() => '{id: $id, field: $field}';\n}\n\nDateTime local(int year, [int month = 1, int day = 1]) {\n  return DateTime(year, month, day);\n}\n\nDateTime utc(int year, [int month = 1, int day = 1]) {\n  return local(year, month, day).toUtc();\n}\n\nvoid main() {\n  group('DateTime filter', () {\n    late Isar isar;\n    late IsarCollection<DateTimeModel> col;\n\n    late DateTimeModel obj1;\n    late DateTimeModel obj2;\n    late DateTimeModel obj3;\n    late DateTimeModel obj4;\n    late DateTimeModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([DateTimeModelSchema]);\n      col = isar.dateTimeModels;\n\n      obj1 = DateTimeModel(local(2010));\n      obj2 = DateTimeModel(local(2020));\n      obj3 = DateTimeModel(local(2010));\n      obj4 = DateTimeModel(utc(2040));\n      objNull = DateTimeModel(null);\n\n      await isar.writeTxn(() async {\n        await isar.dateTimeModels.putAll([obj1, obj2, obj3, obj4, objNull]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.filter().fieldEqualTo(local(2010)), [obj1, obj3]);\n      await qEqual(col.filter().fieldEqualTo(utc(2010)), [obj1, obj3]);\n      await qEqual(col.filter().fieldEqualTo(null), [objNull]);\n      await qEqual(col.filter().fieldEqualTo(local(2027)), []);\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(col.filter().fieldGreaterThan(local(2010)), [obj2, obj4]);\n      await qEqual(\n        col.filter().fieldGreaterThan(local(2010), include: true),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(null),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(null, include: true),\n        [obj1, obj2, obj3, obj4, objNull],\n      );\n      await qEqual(col.filter().fieldGreaterThan(local(2050)), []);\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(\n        col.filter().fieldLessThan(local(2020)),\n        [obj1, obj3, objNull],\n      );\n      await qEqual(\n        col.filter().fieldLessThan(local(2020), include: true),\n        [obj1, obj2, obj3, objNull],\n      );\n      await qEqual(col.filter().fieldLessThan(null), []);\n      await qEqual(col.filter().fieldLessThan(null, include: true), [objNull]);\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.filter().fieldBetween(null, local(2010)),\n        [obj1, obj3, objNull],\n      );\n      await qEqual(\n        col.filter().fieldBetween(null, local(2020), includeLower: false),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(\n        col.filter().fieldBetween(null, local(2020), includeUpper: false),\n        [obj1, obj3, objNull],\n      );\n      await qEqual(col.filter().fieldBetween(local(2030), local(2035)), []);\n      await qEqual(col.filter().fieldBetween(local(2020), local(2000)), []);\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.filter().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(col.filter().fieldIsNotNull(), [obj1, obj2, obj3, obj4]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_embedded_list_test.dart",
    "content": "// ignore_for_file: hash_and_equals\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_embedded_list_test.g.dart';\n\n@collection\nclass Model {\n  Model({\n    required this.embeddedAs,\n    required this.embeddedBs,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  List<EmbeddedA> embeddedAs;\n\n  List<EmbeddedB?>? embeddedBs;\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(embeddedAs, other.embeddedAs) &&\n          listEquals(embeddedBs, other.embeddedBs);\n\n  @override\n  String toString() {\n    return 'Model{id: $id, embeddedAs: $embeddedAs, embeddedBs: $embeddedBs}';\n  }\n}\n\n@embedded\nclass EmbeddedA {\n  EmbeddedA({\n    this.name = '',\n    this.embeddedBs = const [],\n  });\n\n  String name;\n\n  final List<EmbeddedB?>? embeddedBs;\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is EmbeddedA &&\n          runtimeType == other.runtimeType &&\n          name == other.name &&\n          listEquals(embeddedBs, other.embeddedBs);\n\n  @override\n  String toString() {\n    return 'EmbeddedA{name: $name, embeddedBs: $embeddedBs}';\n  }\n}\n\n@embedded\nclass EmbeddedB {\n  EmbeddedB({\n    this.name = '',\n  });\n\n  String name;\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is EmbeddedB &&\n          runtimeType == other.runtimeType &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'EmbeddedB{name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter embedded list', () {\n    late Isar isar;\n\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n    late Model obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      obj1 = Model(\n        embeddedAs: [\n          EmbeddedA(\n            name: 'embedded a1 - 1',\n            embeddedBs: null,\n          ),\n          EmbeddedA(\n            name: 'embedded a1 - 2',\n            embeddedBs: [null, null],\n          ),\n          EmbeddedA(\n            name: 'embedded a1 - 3',\n            embeddedBs: [\n              EmbeddedB(\n                name: 'embedded a1 b1 - 1',\n              ),\n              EmbeddedB(\n                name: 'embedded a1 b1 - 2',\n              ),\n              EmbeddedB(\n                name: 'embedded a1 b1 - 3',\n              ),\n            ],\n          ),\n        ],\n        embeddedBs: [\n          EmbeddedB(name: 'embedded b1 - 1'),\n          EmbeddedB(name: 'embedded b1 - 2'),\n          EmbeddedB(name: 'embedded b1 - 3'),\n        ],\n      );\n      obj2 = Model(\n        embeddedAs: [\n          EmbeddedA(\n            name: 'embedded a2 - 1',\n            embeddedBs: null,\n          ),\n        ],\n        embeddedBs: [null],\n      );\n      obj3 = Model(\n        embeddedAs: [\n          EmbeddedA(\n            name: 'embedded a3 - 1',\n            embeddedBs: [\n              null,\n              EmbeddedB(name: 'embedded a3 b3 - 1'),\n            ],\n          ),\n        ],\n        embeddedBs: null,\n      );\n      obj4 = Model(\n        embeddedAs: [],\n        embeddedBs: [],\n      );\n      obj5 = Model(\n        embeddedAs: [\n          EmbeddedA(\n            name: 'embedded a5 - 1',\n            embeddedBs: [\n              EmbeddedB(name: 'embedded a5 b5 - 1'),\n              EmbeddedB(name: 'embedded a5 b5 - 2'),\n              EmbeddedB(name: 'embedded a5 b5 - 3'),\n              EmbeddedB(name: 'embedded a5 b5 - 4'),\n              EmbeddedB(name: 'embedded a5 b5 - 5'),\n            ],\n          ),\n        ],\n        embeddedBs: [\n          EmbeddedB(name: 'embedded b5 - 1'),\n          EmbeddedB(name: 'embedded b5 - 2'),\n          EmbeddedB(name: 'embedded b5 - 3'),\n          EmbeddedB(name: 'embedded b5 - 4'),\n          EmbeddedB(name: 'embedded b5 - 5'),\n        ],\n      );\n      obj6 = Model(\n        embeddedAs: [\n          EmbeddedA(\n            name: 'embedded a6 - 1',\n            embeddedBs: [\n              EmbeddedB(name: 'embedded a6 b6 - 1'),\n              null,\n              null,\n              null,\n            ],\n          ),\n        ],\n        embeddedBs: [\n          null,\n          EmbeddedB(name: 'embedded b6 - 1'),\n          null,\n        ],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n    });\n\n    isarTest('.embeddedIsNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedBsIsNull(),\n        [obj3],\n      );\n    });\n\n    isarTest('.embeddedIsNotNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedBsIsNotNull(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedLengthEqualTo()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthEqualTo(0),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthEqualTo(1),\n        [obj2, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthEqualTo(3),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthEqualTo(4),\n        [],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(0),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(1),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(2),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(3),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(4),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(5),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthEqualTo(6),\n        [],\n      );\n    });\n\n    isarTest('.embeddedLengthGreaterThan()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthGreaterThan(0),\n        [obj1, obj2, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthGreaterThan(1),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthGreaterThan(2),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthGreaterThan(3, include: true),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthGreaterThan(3),\n        [],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(0),\n        [obj1, obj2, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(1),\n        [obj1, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(2),\n        [obj1, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(3),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(4),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(5, include: true),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthGreaterThan(5),\n        [],\n      );\n    });\n\n    isarTest('.embeddedLengthLessThanThan()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthLessThan(0),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthLessThan(1),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthLessThan(2),\n        [obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthLessThan(3),\n        [obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthLessThan(3),\n        [obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthLessThan(3, include: true),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(0),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(1),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(2),\n        [obj2, obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(3),\n        [obj2, obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(4),\n        [obj1, obj2, obj4, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(5),\n        [obj1, obj2, obj4, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthLessThan(5, include: true),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedLengthBetweenThan()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthBetween(1, 3),\n        [obj1, obj2, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthBetween(3, 4),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsLengthBetween(10, 12),\n        [],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthBetween(1, 3),\n        [obj1, obj2, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthBetween(3, 4),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedBsLengthBetween(10, 12),\n        [],\n      );\n    });\n\n    isarTest('.embeddedIsEmpty()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsIsEmpty(),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedBsIsEmpty(),\n        [obj4],\n      );\n    });\n\n    isarTest('.embeddedIsNotEmpty()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsIsNotEmpty(),\n        [obj1, obj2, obj3, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedBsIsNotEmpty(),\n        [obj1, obj2, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedElementIsNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedBsElementIsNull(),\n        [obj2, obj6],\n      );\n    });\n\n    isarTest('.embeddedElementIsNotNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedBsElementIsNotNull(),\n        [obj1, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedElement()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameStartsWith('embedded')),\n        [obj1, obj2, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a1 - 1')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a1 - 2')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a1 - 3')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a2 - 1')),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a3 - 1')),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a5 - 1')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('embedded a6 - 1')),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameStartsWith('embedded')),\n        [obj1, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b1 - 1')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b1 - 2')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b1 - 3')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b5 - 1')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b5 - 2')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b5 - 3')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b5 - 4')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b5 - 5')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('embedded b6 - 1')),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedBsElement((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedIsNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement((q) => q.embeddedBsIsNull()),\n        [obj1, obj2],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedIsNotNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement((q) => q.embeddedBsIsNotNull()),\n        [obj1, obj3, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedLengthEqualTo()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(0)),\n        [],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(1)),\n        [],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(2)),\n        [obj1, obj3],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(3)),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(4)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(5)),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthEqualTo(6)),\n        [],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedLengthLessThan()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(0)),\n        [],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(1)),\n        [],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(2)),\n        [],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(3)),\n        [obj1, obj3],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(4)),\n        [obj1, obj3],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(5)),\n        [obj1, obj3, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(6)),\n        [obj1, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthLessThan(7)),\n        [obj1, obj3, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedLengthGreaterThan()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthGreaterThan(0)),\n        [obj1, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthGreaterThan(1)),\n        [obj1, obj3, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthGreaterThan(2)),\n        [obj1, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthGreaterThan(3)),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthGreaterThan(4)),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthGreaterThan(5)),\n        [],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedLengthBetween()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthBetween(2, 4)),\n        [obj1, obj3, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthBetween(3, 4)),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsLengthBetween(5, 42)),\n        [obj5],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedIsEmpty()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement((q) => q.embeddedBsIsEmpty()),\n        [],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedIsNotEmpty()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement((q) => q.embeddedBsIsNotEmpty()),\n        [obj1, obj3, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedElementIsNull()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsElementIsNull()),\n        [obj1, obj3, obj6],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedElementIsNotNull()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .embeddedAsElement((q) => q.embeddedBsElementIsNotNull()),\n        [obj1, obj3, obj5, obj6],\n      );\n    });\n\n    isarTest('.embeddedElement() then .embeddedElement()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameStartsWith('embedded'),\n              ),\n            ),\n        [obj1, obj3, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a1 b1 - 1'),\n              ),\n            ),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a1 b1 - 2'),\n              ),\n            ),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a1 b1 - 3'),\n              ),\n            ),\n        [obj1],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a3 b3 - 1'),\n              ),\n            ),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a5 b5 - 1'),\n              ),\n            ),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a5 b5 - 2'),\n              ),\n            ),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a5 b5 - 3'),\n              ),\n            ),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a5 b5 - 4'),\n              ),\n            ),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a5 b5 - 5'),\n              ),\n            ),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('embedded a6 b6 - 1'),\n              ),\n            ),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedAsElement(\n              (q) => q.embeddedBsElement(\n                (q) => q.nameEqualTo('non existing'),\n              ),\n            ),\n        [],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_embedded_test.dart",
    "content": "// ignore_for_file: hash_and_equals\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_embedded_test.g.dart';\n\n@collection\nclass Model {\n  Model({\n    required this.embeddedA,\n    required this.embeddedB,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  EmbeddedModelA embeddedA;\n\n  EmbeddedModelB? embeddedB;\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          embeddedA == other.embeddedA &&\n          embeddedB == other.embeddedB;\n\n  @override\n  String toString() {\n    return '''Model{id: $id, embeddedA: $embeddedA, embeddedB: $embeddedB}''';\n  }\n}\n\n@embedded\nclass EmbeddedModelA {\n  const EmbeddedModelA({\n    this.name = '',\n    this.embeddedB = const EmbeddedModelB(),\n  });\n\n  final String name;\n\n  final EmbeddedModelB? embeddedB;\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is EmbeddedModelA &&\n          runtimeType == other.runtimeType &&\n          name == other.name &&\n          embeddedB == other.embeddedB;\n\n  @override\n  String toString() {\n    return 'EmbeddedModelA{name: $name, embeddedB: $embeddedB}';\n  }\n}\n\n@embedded\nclass EmbeddedModelB {\n  const EmbeddedModelB({\n    this.name = '',\n  });\n\n  final String name;\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is EmbeddedModelB &&\n          runtimeType == other.runtimeType &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'EmbeddedModelB{name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter embedded', () {\n    late Isar isar;\n\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n    late Model obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      obj1 = Model(\n        embeddedA: const EmbeddedModelA(\n          name: 'embedded a1',\n          embeddedB: EmbeddedModelB(name: 'embedded a1 b1'),\n        ),\n        embeddedB: const EmbeddedModelB(name: 'embedded b1'),\n      );\n      obj2 = Model(\n        embeddedA: const EmbeddedModelA(\n          name: 'embedded a2',\n          embeddedB: EmbeddedModelB(name: 'embedded a2 b2'),\n        ),\n        embeddedB: null,\n      );\n      obj3 = Model(\n        embeddedA: const EmbeddedModelA(\n          name: 'embedded a3',\n          embeddedB: null,\n        ),\n        embeddedB: const EmbeddedModelB(name: 'embedded b3'),\n      );\n      obj4 = Model(\n        embeddedA: const EmbeddedModelA(\n          name: 'embedded a4',\n          embeddedB: EmbeddedModelB(name: 'embedded a4 b4'),\n        ),\n        embeddedB: const EmbeddedModelB(name: 'embedded b4'),\n      );\n      obj5 = Model(\n        embeddedA: const EmbeddedModelA(\n          name: 'embedded a5',\n          embeddedB: null,\n        ),\n        embeddedB: const EmbeddedModelB(name: 'embedded b5'),\n      );\n      obj6 = Model(\n        embeddedA: const EmbeddedModelA(\n          name: 'embedded a6',\n          embeddedB: EmbeddedModelB(name: 'embedded a6 b6'),\n        ),\n        embeddedB: null,\n      );\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n    });\n\n    isarTest('.embedded()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameStartsWith('embedded')),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded a1')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded a2')),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded a3')),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded a4')),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded a5')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded a6')),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.nameEqualTo('embedded b1')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameStartsWith('embedded')),\n        [obj1, obj3, obj4, obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded b1')),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded b2')),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded b3')),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded b4')),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded b5')),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded b6')),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedB((q) => q.nameEqualTo('embedded a1')),\n        [],\n      );\n    });\n\n    isarTest('.embeddedIsNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedBIsNull(),\n        [obj2, obj6],\n      );\n    });\n\n    isarTest('.embeddedIsNotNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedBIsNotNull(),\n        [obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('.embedded() then .embedded()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameStartsWith('embedded'),\n              ),\n            ),\n        [obj1, obj2, obj4, obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('embedded a1 b1'),\n              ),\n            ),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('embedded a2 b2'),\n              ),\n            ),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('embedded a3 b3'),\n              ),\n            ),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('embedded a4 b4'),\n              ),\n            ),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('embedded a5 b5'),\n              ),\n            ),\n        [],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('embedded a6 b6'),\n              ),\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.models.filter().embeddedA(\n              (q) => q.embeddedB(\n                (q) => q.nameEqualTo('non existing'),\n              ),\n            ),\n        [],\n      );\n    });\n\n    isarTest('.embedded() then .embeddedIsNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.embeddedBIsNull()),\n        [obj3, obj5],\n      );\n    });\n\n    isarTest('.embedded() then .embeddedIsNotNull()', () async {\n      await qEqualSet(\n        isar.models.filter().embeddedA((q) => q.embeddedBIsNotNull()),\n        [obj1, obj2, obj4, obj6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_float_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_float_list_test.g.dart';\n\n@collection\nclass FloatModel {\n  FloatModel(this.list);\n\n  Id? id;\n\n  List<float?>? list;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is FloatModel &&\n      id == other.id &&\n      doubleListEquals(other.list, list);\n}\n\nvoid main() {\n  group('Float list filter', () {\n    late Isar isar;\n    late IsarCollection<FloatModel> col;\n\n    late FloatModel objEmpty;\n    late FloatModel obj1;\n    late FloatModel obj2;\n    late FloatModel obj3;\n    late FloatModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([FloatModelSchema]);\n      col = isar.floatModels;\n\n      objEmpty = FloatModel([]);\n      obj1 = FloatModel([1.1, 3.3]);\n      obj2 = FloatModel([null]);\n      obj3 = FloatModel([null, -1000]);\n      objNull = FloatModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objEmpty, obj1, obj2, obj3, objNull]);\n      });\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqual(col.filter().listElementEqualTo(1.1), [obj1]);\n      await qEqual(col.filter().listElementEqualTo(4), []);\n      await qEqual(col.filter().listElementEqualTo(null), [obj2, obj3]);\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqual(col.filter().listElementGreaterThan(3.3), []);\n      await qEqual(\n        col.filter().listElementGreaterThan(3.3, include: true),\n        [obj1],\n      );\n      await qEqual(\n        col.filter().listElementGreaterThan(3.4, include: true, epsilon: 0.2),\n        [obj1],\n      );\n      await qEqual(col.filter().listElementGreaterThan(4), []);\n      await qEqual(col.filter().listElementGreaterThan(null), [obj1, obj3]);\n      await qEqual(\n        col.filter().listElementGreaterThan(null, include: true),\n        [obj1, obj2, obj3],\n      );\n    });\n\n    isarTest('.elementLessThan()', () async {\n      await qEqual(col.filter().listElementLessThan(1.1), [obj2, obj3]);\n      await qEqual(\n        col.filter().listElementLessThan(1.1, include: true),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(\n        col.filter().listElementLessThan(1, include: true, epsilon: 0.2),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(col.filter().listElementLessThan(null), []);\n      await qEqual(\n        col.filter().listElementLessThan(null, include: true),\n        [obj2, obj3],\n      );\n    });\n\n    isarTest('.anyBetween()', () async {\n      await qEqual(col.filter().listElementBetween(1, 5), [obj1]);\n      await qEqual(\n        col.filter().listElementBetween(null, 1.1),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(\n        col.filter().listElementBetween(null, 1.1, includeLower: false),\n        [obj1, obj3],\n      );\n      await qEqual(\n        col.filter().listElementBetween(null, 1.1, includeUpper: false),\n        [obj2, obj3],\n      );\n      await qEqual(\n        col.filter().listElementBetween(\n              null,\n              1.1,\n              includeLower: false,\n              includeUpper: false,\n            ),\n        [obj3],\n      );\n      await qEqual(col.filter().listElementBetween(5, 10), []);\n      await qEqual(col.filter().listElementBetween(null, null), [obj2, obj3]);\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqual(col.filter().listElementIsNull(), [obj2, obj3]);\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqual(col.filter().listElementIsNotNull(), [obj1, obj3]);\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.filter().listIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(col.filter().listIsNotNull(), [objEmpty, obj1, obj2, obj3]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_float_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_float_test.g.dart';\n\n@collection\nclass FloatModel {\n  FloatModel(this.field);\n\n  Id? id;\n\n  float? field = 0;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is FloatModel && id == other.id && doubleEquals(field, other.field);\n}\n\nvoid main() {\n  group('Float filter', () {\n    late Isar isar;\n    late IsarCollection<FloatModel> col;\n\n    late FloatModel obj1;\n    late FloatModel obj2;\n    late FloatModel obj3;\n    late FloatModel objNInf;\n    late FloatModel objInf;\n    late FloatModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([FloatModelSchema]);\n      col = isar.floatModels;\n\n      obj1 = FloatModel(1.1);\n      obj2 = FloatModel(2.2);\n      obj3 = FloatModel(3.3);\n      objNInf = FloatModel(double.negativeInfinity);\n      objInf = FloatModel(double.infinity);\n      objNull = FloatModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objInf, obj2, obj1, obj3, objNInf, objNull]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.filter().fieldEqualTo(2.2), [obj2]);\n      await qEqual(col.filter().fieldEqualTo(2.1), []);\n      await qEqual(col.filter().fieldEqualTo(2.1, epsilon: 0.2), [obj2]);\n      await qEqual(col.filter().fieldEqualTo(2.3, epsilon: 0.2), [obj2]);\n      await qEqual(col.filter().fieldEqualTo(null), [objNull]);\n      await qEqual(col.filter().fieldEqualTo(double.infinity), [objInf]);\n      await qEqual(\n        col.filter().fieldEqualTo(double.negativeInfinity),\n        [objNInf],\n      );\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(\n        col.filter().fieldGreaterThan(null),\n        [objInf, obj2, obj1, obj3, objNInf],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(null, include: true),\n        [objInf, obj2, obj1, obj3, objNInf, objNull],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(double.negativeInfinity),\n        [objInf, obj2, obj1, obj3],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(double.negativeInfinity, include: true),\n        [objInf, obj2, obj1, obj3, objNInf],\n      );\n      await qEqual(col.filter().fieldGreaterThan(2.2), [objInf, obj3]);\n      await qEqual(\n        col.filter().fieldGreaterThan(2.2, include: true),\n        [objInf, obj2, obj3],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(2.3, epsilon: 0.2, include: true),\n        [objInf, obj2, obj3],\n      );\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.filter().fieldLessThan(null), []);\n      await qEqual(col.filter().fieldLessThan(null, include: true), [objNull]);\n      await qEqual(\n        col.filter().fieldLessThan(double.negativeInfinity),\n        [objNull],\n      );\n      await qEqual(\n        col.filter().fieldLessThan(double.negativeInfinity, include: true),\n        [objNInf, objNull],\n      );\n      await qEqual(col.filter().fieldLessThan(1.1), [objNInf, objNull]);\n      await qEqual(\n        col.filter().fieldLessThan(1.1, include: true),\n        [obj1, objNInf, objNull],\n      );\n      await qEqual(\n        col.filter().fieldLessThan(1.2, epsilon: 0.2),\n        [objNInf, objNull],\n      );\n      await qEqual(\n        col.filter().fieldLessThan(1, include: true, epsilon: 0.2),\n        [obj1, objNInf, objNull],\n      );\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(col.filter().fieldBetween(null, null), [objNull]);\n      await qEqual(col.filter().fieldBetween(1.1, 3.3), [obj2, obj1, obj3]);\n      await qEqual(\n        col.filter().fieldBetween(1.1, 3.3, includeLower: false),\n        [obj2, obj3],\n      );\n      await qEqual(\n        col.filter().fieldBetween(1.1, 3.3, includeUpper: false),\n        [obj2, obj1],\n      );\n      await qEqual(\n        col\n            .filter()\n            .fieldBetween(1.1, 3.3, includeLower: false, includeUpper: false),\n        [obj2],\n      );\n      await qEqual(\n        col.filter().fieldBetween(1.2, 3.2, epsilon: 0.2),\n        [obj2, obj1, obj3],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.filter().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(\n        col.filter().fieldIsNotNull(),\n        [objInf, obj2, obj1, obj3, objNInf],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_id_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_id_test.g.dart';\n\n@collection\nclass IdModel {\n  IdModel();\n\n  Id? id;\n\n  @override\n  String toString() {\n    return '{id: $id}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is IdModel && other.id == id;\n  }\n}\n\nvoid main() {\n  group('Id filter', () {\n    late Isar isar;\n    late IsarCollection<IdModel> col;\n\n    late IdModel obj0;\n    late IdModel obj1;\n    late IdModel obj2;\n    late IdModel obj3;\n\n    setUp(() async {\n      isar = await openTempIsar([IdModelSchema]);\n      col = isar.idModels;\n\n      obj0 = IdModel()..id = 0;\n      obj1 = IdModel()..id = 1;\n      obj2 = IdModel()..id = 2;\n      obj3 = IdModel()..id = 3;\n\n      await isar.writeTxn(() async {\n        await isar.idModels.putAll([obj0, obj2, obj3, obj1]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.filter().idEqualTo(2), [obj2]);\n      await qEqual(col.filter().idEqualTo(5), []);\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(col.filter().idGreaterThan(2), [obj3]);\n      await qEqual(col.filter().idGreaterThan(2, include: true), [obj2, obj3]);\n      await qEqual(col.filter().idGreaterThan(3), []);\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.filter().idLessThan(1), [obj0]);\n      await qEqual(col.filter().idLessThan(1, include: true), [obj0, obj1]);\n      await qEqual(col.filter().idLessThan(0), []);\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(col.filter().idBetween(1, 3), [obj1, obj2, obj3]);\n      await qEqual(\n        col.filter().idBetween(1, 3, includeLower: false),\n        [obj2, obj3],\n      );\n      await qEqual(\n        col.filter().idBetween(1, 3, includeUpper: false),\n        [obj1, obj2],\n      );\n      await qEqual(\n        col.filter().idBetween(1, 3, includeLower: false, includeUpper: false),\n        [obj2],\n      );\n      await qEqual(col.filter().idBetween(5, 6), []);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_int_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_int_test.g.dart';\n\n@collection\nclass IntModel {\n  IntModel(this.field);\n\n  Id? id;\n\n  short? field = 0;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is IntModel && id == other.id && other.field == field;\n  }\n}\n\nvoid main() {\n  group('Int filter', () {\n    late Isar isar;\n    late IsarCollection<IntModel> col;\n\n    late IntModel obj0;\n    late IntModel obj1;\n    late IntModel obj2;\n    late IntModel obj3;\n    late IntModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([IntModelSchema]);\n      col = isar.intModels;\n\n      objNull = IntModel(null);\n      obj0 = IntModel(-1234);\n      obj1 = IntModel(1);\n      obj2 = IntModel(2);\n      obj3 = IntModel(1);\n\n      await isar.writeTxn(() async {\n        await isar.intModels.putAll([obj0, obj1, obj2, obj3, objNull]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.filter().fieldEqualTo(2), [obj2]);\n      await qEqual(col.filter().fieldEqualTo(null), [objNull]);\n      await qEqual(col.filter().fieldEqualTo(5), []);\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(col.filter().fieldGreaterThan(1), [obj2]);\n      await qEqual(\n        col.filter().fieldGreaterThan(1, include: true),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(null),\n        [obj0, obj1, obj2, obj3],\n      );\n      await qEqual(\n        col.filter().fieldGreaterThan(null, include: true),\n        [obj0, obj1, obj2, obj3, objNull],\n      );\n      await qEqual(col.filter().fieldGreaterThan(4), []);\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.filter().fieldLessThan(1), [obj0, objNull]);\n      await qEqual(col.filter().fieldLessThan(null), []);\n      await qEqual(col.filter().fieldLessThan(null, include: true), [objNull]);\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(col.filter().fieldBetween(1, 2), [obj1, obj2, obj3]);\n      await qEqual(\n        col.filter().fieldBetween(1, 2, includeLower: false),\n        [obj2],\n      );\n      await qEqual(\n        col.filter().fieldBetween(1, 2, includeUpper: false),\n        [obj1, obj3],\n      );\n      await qEqual(\n        col\n            .filter()\n            .fieldBetween(1, 2, includeLower: false, includeUpper: false),\n        [],\n      );\n      await qEqual(\n        col.filter().fieldBetween(null, 1),\n        [obj0, obj1, obj3, objNull],\n      );\n      await qEqual(col.filter().fieldBetween(5, 6), []);\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.filter().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(col.filter().fieldIsNotNull(), [obj0, obj1, obj2, obj3]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_list_length_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_list_length_test.g.dart';\n\n@collection\nclass Model {\n  Model({\n    required this.bools,\n    required this.ints,\n    required this.doubles,\n    required this.strings,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  final List<bool> bools;\n\n  @Index()\n  final List<int>? ints;\n\n  final List<double?> doubles;\n\n  final List<String?>? strings;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(bools, other.bools) &&\n          listEquals(ints, other.ints) &&\n          listEquals(doubles, other.doubles) &&\n          listEquals(strings, other.strings);\n\n  @override\n  String toString() {\n    return '''Model{id: $id, bools: $bools, ints: $ints, doubles: $doubles, strings: $strings}''';\n  }\n}\n\nvoid main() {\n  group('Filter list length', () {\n    late Isar isar;\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      obj1 = Model(\n        bools: [],\n        ints: [1, 42, -128],\n        doubles: [1.123, 4, 5.333, 1.22, 1.22, 1.22],\n        strings: null,\n      );\n      obj2 = Model(\n        bools: [true, true, false],\n        ints: [4],\n        doubles: [null],\n        strings: ['Foo', 'bar'],\n      );\n      obj3 = Model(\n        bools: [true],\n        ints: [0, 123],\n        doubles: [3.141592653, null, null],\n        strings: ['a', 'b', 'c', 'd'],\n      );\n      obj4 = Model(\n        bools: [true, true, false, true],\n        ints: [-1, 1],\n        doubles: [4, 12.2, 12.43],\n        strings: ['', 'abc', null],\n      );\n      obj5 = Model(\n        bools: [true, true, true, true, true, false],\n        ints: [],\n        doubles: [],\n        strings: [null, null],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll([obj1, obj2, obj3, obj4, obj5]),\n      );\n    });\n\n    isarTest('.lengthEqualTo()', () async {\n      await qEqual(isar.models.filter().boolsLengthEqualTo(1), [obj3]);\n      await qEqual(isar.models.filter().intsLengthEqualTo(2), [obj3, obj4]);\n      await qEqual(isar.models.filter().doublesLengthEqualTo(6), [obj1]);\n      await qEqual(isar.models.filter().doublesLengthEqualTo(42), []);\n      await qEqual(\n        isar.models.filter().doublesLengthEqualTo(9223372036854775807),\n        [],\n      );\n    });\n\n    isarTest('.lengthGreaterThan()', () async {\n      await qEqual(\n        isar.models.filter().boolsLengthGreaterThan(3),\n        [obj4, obj5],\n      );\n      await qEqual(\n        isar.models.filter().boolsLengthGreaterThan(4),\n        [obj5],\n      );\n      await qEqual(\n        isar.models.filter().boolsLengthGreaterThan(4, include: true),\n        [obj4, obj5],\n      );\n      await qEqual(isar.models.filter().intsLengthGreaterThan(3), []);\n      await qEqual(\n        isar.models.filter().boolsLengthGreaterThan(0),\n        [obj2, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('.lengthLessThan()', () async {\n      await qEqual(isar.models.filter().boolsLengthLessThan(3), [obj1, obj3]);\n      await qEqual(\n        isar.models.filter().boolsLengthLessThan(3, include: true),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(isar.models.filter().intsLengthLessThan(2), [obj2, obj5]);\n      await qEqual(\n        isar.models.filter().doublesLengthLessThan(0, include: true),\n        [obj5],\n      );\n      await qEqual(\n        isar.models.filter().stringsLengthLessThan(42),\n        [obj2, obj3, obj4, obj5],\n      );\n      await qEqual(isar.models.filter().stringsLengthLessThan(2), []);\n    });\n\n    isarTest('.lengthBetween()', () async {\n      await qEqual(\n        isar.models.filter().boolsLengthBetween(2, 6),\n        [obj2, obj4, obj5],\n      );\n      await qEqual(\n        isar.models.filter().boolsLengthBetween(2, 6, includeUpper: false),\n        [obj2, obj4],\n      );\n      await qEqual(\n        isar.models.filter().intsLengthBetween(0, 42),\n        [obj1, obj2, obj3, obj4, obj5],\n      );\n      await qEqual(\n        isar.models.filter().intsLengthBetween(0, 42, includeLower: false),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(\n        isar.models.filter().doublesLengthBetween(2, 3),\n        [obj3, obj4],\n      );\n      await qEqual(isar.models.filter().doublesLengthBetween(0, 0), [obj5]);\n      await qEqual(\n        isar.models.filter().doublesLengthBetween(\n              0,\n              1,\n              includeLower: false,\n            ),\n        [obj2],\n      );\n      await qEqual(isar.models.filter().stringsLengthLessThan(3), [obj2, obj5]);\n    });\n\n    isarTest('.isEmpty()', () async {\n      await qEqual(isar.models.filter().boolsIsEmpty(), [obj1]);\n      await qEqual(isar.models.filter().intsIsEmpty(), [obj5]);\n      await qEqual(isar.models.filter().doublesIsEmpty(), [obj5]);\n      await qEqual(isar.models.filter().stringsIsEmpty(), []);\n    });\n\n    isarTest('.isNotEmpty()', () async {\n      await qEqual(\n        isar.models.filter().boolsIsNotEmpty(),\n        [obj2, obj3, obj4, obj5],\n      );\n      await qEqual(\n        isar.models.filter().intsIsNotEmpty(),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(\n        isar.models.filter().doublesIsNotEmpty(),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(\n        isar.models.filter().stringsIsNotEmpty(),\n        [obj2, obj3, obj4, obj5],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_string_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_string_list_test.g.dart';\n\n@collection\nclass StringModel {\n  StringModel({\n    required this.strings,\n    required this.nullableStrings,\n    required this.stringsNullable,\n    required this.nullableStringsNullable,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  List<String> strings;\n  List<String?> nullableStrings;\n  List<String>? stringsNullable;\n  List<String?>? nullableStringsNullable;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is StringModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(strings, other.strings) &&\n          listEquals(nullableStrings, other.nullableStrings) &&\n          listEquals(stringsNullable, other.stringsNullable) &&\n          listEquals(nullableStringsNullable, other.nullableStringsNullable);\n\n  @override\n  String toString() {\n    return '''StringModel{id: $id, strings: $strings, nullableStrings: $nullableStrings, stringsNullable: $stringsNullable, nullableStringsNullable: $nullableStringsNullable}''';\n  }\n}\n\nvoid main() {\n  group('String list filter', () {\n    late Isar isar;\n\n    late StringModel obj1;\n    late StringModel obj2;\n    late StringModel obj3;\n    late StringModel obj4;\n    late StringModel obj5;\n    late StringModel obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([StringModelSchema]);\n\n      obj1 = StringModel(\n        strings: ['strings 1', 'strings 2', 'strings 3'],\n        nullableStrings: ['nullable strings 1', null, 'nullable strings 3'],\n        stringsNullable: ['strings nullable 1'],\n        nullableStringsNullable: ['nullable strings nullable 1', null, null],\n      );\n      obj2 = StringModel(\n        strings: ['strings 2', 'strings 4'],\n        nullableStrings: [\n          'nullable strings 2',\n          'nullable strings 3',\n          'nullable strings 3',\n        ],\n        stringsNullable: null,\n        nullableStringsNullable: null,\n      );\n      obj3 = StringModel(\n        strings: [],\n        nullableStrings: [],\n        stringsNullable: [],\n        nullableStringsNullable: [],\n      );\n      obj4 = StringModel(\n        strings: ['strings 1', 'strings 5', 'strings 6'],\n        nullableStrings: ['nullable strings 4', 'nullable strings 5'],\n        stringsNullable: [\n          'strings nullable 4',\n          'strings nullable 5',\n          'strings nullable 6',\n        ],\n        nullableStringsNullable: [null, null, null],\n      );\n      obj5 = StringModel(\n        strings: [\n          'strings 3',\n          'strings 4',\n          'strings 5',\n          'strings 6',\n          'strings 7',\n        ],\n        nullableStrings: [\n          null,\n          'nullable strings 3',\n          'nullable strings 4',\n          'nullable strings 5',\n          'nullable strings 6',\n        ],\n        stringsNullable: ['strings nullable 1'],\n        nullableStringsNullable: null,\n      );\n      obj6 = StringModel(\n        strings: [''],\n        nullableStrings: [\n          '',\n          'nullable strings 2',\n          'nullable strings 5',\n          'nullable strings 6',\n        ],\n        stringsNullable: ['strings nullable 4', 'strings nullable 5', ''],\n        nullableStringsNullable: [\n          null,\n          '',\n          'nullable strings nullable 3',\n          'nullable strings nullable 5',\n        ],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.stringModels.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 1'),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 2'),\n        [obj1, obj2],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 3'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 4'),\n        [obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 5'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 6'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('strings 7'),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('nullable strings 1'),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('nullable strings 2'),\n        [obj2, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('nullable strings 3'),\n        [obj1, obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('nullable strings 4'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('nullable strings 5'),\n        [obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('nullable strings 6'),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEqualTo('strings nullable 1'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEqualTo('strings nullable 4'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEqualTo('strings nullable 5'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEqualTo('strings nullable 6'),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementEqualTo(\n              'nullable strings nullable 1',\n            ),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementEqualTo(\n              'nullable strings nullable 3',\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementEqualTo(\n              'nullable strings nullable 5',\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementEqualTo('non existing'),\n        [],\n      );\n    });\n\n    isarTest('.elementStartWith()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementStartsWith('strings'),\n        [obj1, obj2, obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementStartsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementStartsWith('nullable'),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementStartsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementStartsWith('strings'),\n        [obj1, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementStartsWith('nullable'),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementStartsWith('non existing'),\n        [],\n      );\n    });\n\n    isarTest('.elementEndsWith()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('1'),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('2'),\n        [obj1, obj2],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('3'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('4'),\n        [obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('5'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('6'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('7'),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementEndsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementEndsWith('1'),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementEndsWith('2'),\n        [obj2, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementEndsWith('3'),\n        [obj1, obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementEndsWith('4'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementEndsWith('5'),\n        [obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementEndsWith('6'),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementEndsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementEndsWith('1'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementEndsWith('4'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementEndsWith('5'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementEndsWith('6'),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementEndsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementEndsWith('1'),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementEndsWith('3'),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementEndsWith('5'),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementEndsWith('non existing'),\n        [],\n      );\n    });\n\n    isarTest('.elementContains()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementContains('ings'),\n        [obj1, obj2, obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementContains('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementContains('ings'),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementContains('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementContains('ings'),\n        [obj1, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementContains('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementContains('ings'),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementContains('non existing'),\n        [],\n      );\n    });\n\n    isarTestVm('.elementMatches() VM', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementMatches('?????????'),\n        [obj1, obj2, obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementMatches('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementMatches('??????????????????'),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementMatches('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementMatches('??????????????????'),\n        [obj1, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementMatches('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementMatches(\n              '???????????????????????????',\n            ),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsNullableElementMatches('non existing'),\n        [],\n      );\n    });\n\n    isarTestWeb('.elementMatches() WEB', () async {\n      expect(\n        await isar.stringModels\n            .filter()\n            .stringsElementMatches('?????????')\n            .tFindAll(),\n        anyOf(throwsUnimplementedError, throwsUnsupportedError),\n      );\n\n      expect(\n        await isar.stringModels\n            .filter()\n            .nullableStringsElementMatches('??????????????????')\n            .tFindAll(),\n        anyOf(throwsUnimplementedError, throwsUnsupportedError),\n      );\n\n      expect(\n        await isar.stringModels\n            .filter()\n            .stringsNullableElementMatches('??????????????????')\n            .tFindAll(),\n        anyOf(throwsUnimplementedError, throwsUnsupportedError),\n      );\n\n      expect(\n        await isar.stringModels\n            .filter()\n            .nullableStringsNullableElementContains(\n              '???????????????????????????',\n            )\n            .tFindAll(),\n        anyOf(throwsUnimplementedError, throwsUnsupportedError),\n      );\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementIsNull(),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementIsNull(),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementIsNotNull(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementIsNotNull(),\n        [obj1, obj6],\n      );\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementGreaterThan('strings 3'),\n        [obj2, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementGreaterThan('nullable strings 3'),\n        [obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementGreaterThan('strings nullable 3'),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementGreaterThan(\n              'nullable strings nullable 3',\n            ),\n        [obj6],\n      );\n    });\n\n    // FIXME: .elementLessThan() doesn't seem to be working\n    // returns either no elements or null elements\n    // also works fine in the index test\n    isarTest('.elementLessThan()', () async {\n      /*await qEqualSet(\n        isar.stringModels.filter().stringsElementLessThan('strings 3'),\n        [obj1, obj2, obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .nullableStringsElementLessThan('nullable strings 3'),\n        [obj1, obj2, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsNullableElementLessThan('strings nullable 3'),\n        [obj1, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementLessThan(\n              'nullable strings nullable 3',\n            ),\n        [obj1, obj4, obj6],\n      );*/\n    });\n\n    isarTest('.elementBetween()', () async {\n      await qEqualSet(\n        isar.stringModels\n            .filter()\n            .stringsElementBetween('strings 2', 'strings 4'),\n        [obj1, obj2, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementBetween(\n              'nullable strings 2',\n              'nullable strings 4',\n            ),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementBetween(\n              'strings nullable 2',\n              'strings nullable 4',\n            ),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementBetween(\n              'nullable strings nullable 2',\n              'nullable strings nullable 4',\n            ),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementIsEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementIsEmpty(),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementIsEmpty(),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementIsEmpty(),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementIsEmpty(),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementIsNotEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsElementIsNotEmpty(),\n        [obj1, obj2, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsElementIsNotEmpty(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableElementIsNotEmpty(),\n        [obj1, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableElementIsNotEmpty(),\n        [obj1, obj6],\n      );\n    });\n\n    isarTest('.lengthEqualTo()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthEqualTo(0),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthEqualTo(1),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthEqualTo(2),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthEqualTo(3),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthEqualTo(5),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthEqualTo(0),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthEqualTo(2),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthEqualTo(3),\n        [obj1, obj2],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthEqualTo(4),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthEqualTo(5),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableLengthEqualTo(0),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableLengthEqualTo(1),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableLengthEqualTo(3),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableLengthEqualTo(0),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableLengthEqualTo(3),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableLengthEqualTo(4),\n        [obj6],\n      );\n    });\n\n    isarTest('.lengthGreaterThan()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthGreaterThan(2),\n        [obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthGreaterThan(2),\n        [obj1, obj2, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableLengthGreaterThan(2),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableLengthGreaterThan(2),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.lengthLessThan()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthLessThan(2),\n        [obj3, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthLessThan(2),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableLengthLessThan(2),\n        [obj1, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableLengthLessThan(2),\n        [obj3],\n      );\n    });\n\n    isarTest('.lengthBetween()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsLengthBetween(2, 4),\n        [obj1, obj2, obj4],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsLengthBetween(2, 4),\n        [obj1, obj2, obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableLengthBetween(2, 4),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableLengthBetween(2, 4),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.isEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsIsEmpty(),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsIsEmpty(),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableIsEmpty(),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableIsEmpty(),\n        [obj3],\n      );\n    });\n\n    isarTest('.isNotEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsIsNotEmpty(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsIsNotEmpty(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableIsNotEmpty(),\n        [obj1, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableIsNotEmpty(),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableIsNull(),\n        [obj2],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableIsNull(),\n        [obj2, obj5],\n      );\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().stringsNullableIsNotNull(),\n        [obj1, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableStringsNullableIsNotNull(),\n        [obj1, obj3, obj4, obj6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/filter_string_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_string_test.g.dart';\n\n@collection\nclass StringModel {\n  StringModel(this.field);\n\n  Id id = Isar.autoIncrement;\n\n  String? field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is StringModel && field == other.field;\n\n  @override\n  String toString() {\n    return 'StringModel{id: $id, field: $field}';\n  }\n}\n\nvoid main() {\n  group('String filter', () {\n    late Isar isar;\n\n    late StringModel objEmpty;\n    late StringModel obj1;\n    late StringModel obj2;\n    late StringModel obj3;\n    late StringModel obj4;\n    late StringModel obj5;\n    late StringModel obj6;\n    late StringModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([StringModelSchema]);\n\n      objEmpty = StringModel('');\n      obj1 = StringModel('string 1');\n      obj2 = StringModel('string 2');\n      obj3 = StringModel('string 3');\n      obj4 = StringModel('string 4');\n      obj5 = StringModel('string 5');\n      obj6 = StringModel('string 5');\n      objNull = StringModel(null);\n\n      await isar.writeTxn(\n        () async => isar.stringModels.tPutAll([\n          objEmpty,\n          obj1,\n          obj2,\n          obj3,\n          obj4,\n          obj5,\n          obj6,\n          objNull,\n        ]),\n      );\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(\n        isar.stringModels.filter().fieldEqualTo('string 2'),\n        [obj2],\n      );\n      await qEqual(\n        isar.stringModels.filter().fieldEqualTo(null),\n        [objNull],\n      );\n      await qEqual(\n        isar.stringModels.filter().fieldEqualTo('string 6'),\n        [],\n      );\n      await qEqual(\n        isar.stringModels.filter().fieldEqualTo(''),\n        [objEmpty],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(\n        isar.stringModels.filter().fieldIsNull(),\n        [objNull],\n      );\n    });\n\n    isarTest('.startsWith()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldStartsWith('string'),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().fieldStartsWith(''),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(isar.stringModels.filter().fieldStartsWith('S'), {});\n    });\n\n    isarTest('.endsWith()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldEndsWith('5'),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().fieldEndsWith(''),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(isar.stringModels.filter().fieldEndsWith('8'), []);\n    });\n\n    isarTest('.contains()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldContains('ing'),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().fieldContains(''),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().fieldContains('x'),\n        [],\n      );\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldGreaterThan('string 0'),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldGreaterThan('string 1'),\n        [obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldGreaterThan('string 2'),\n        [obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldGreaterThan('string 3'),\n        [obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldGreaterThan('string 4'),\n        [obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldGreaterThan('string 5'),\n        [],\n      );\n    });\n\n    isarTest('.lessThan()', () async {\n      /* FIXME: lessThan not working\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 0'),\n        [objEmpty, objNull],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 1'),\n        [objEmpty, objNull],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 2'),\n        [objEmpty, objNull, obj1],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 3'),\n        [objEmpty, objNull, obj1, obj2],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 4'),\n        [objEmpty, objNull, obj1, obj2, obj3],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 5'),\n        [objEmpty, objNull, obj1, obj2, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldLessThan('string 6'),\n        [objEmpty, objNull, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      */\n    });\n\n    isarTest('.between()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldBetween('string 2', 'string 4'),\n        [obj2, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().fieldBetween('', 'string 2'),\n        [objEmpty, obj1, obj2],\n      );\n\n      /* FIXME: Something seems to be wrong when\n          between has `includeLower: false`\n      await qEqualSet(\n        isar.stringModels.filter().fieldBetween(\n              '',\n              'string 2',\n              includeLower: false,\n              includeUpper: false,\n            ),\n        [obj1],\n      );\n      */\n    });\n\n    isarTestVm('.matches() VM', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldMatches('*ng 5'),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.filter().fieldMatches('????????'),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(isar.stringModels.filter().fieldMatches(''), [objEmpty]);\n\n      await qEqualSet(isar.stringModels.filter().fieldMatches('*4?'), []);\n    });\n\n    isarTestWeb('.matches() WEB', () async {\n      expect(\n        await isar.stringModels.filter().fieldMatches('*ng 5').tFindAll(),\n        anyOf(throwsUnimplementedError, throwsUnsupportedError),\n      );\n    });\n\n    isarTest('.isEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldIsEmpty(),\n        [objEmpty],\n      );\n    });\n\n    isarTest('.isNotEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().fieldIsNotEmpty(),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_backlink_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_backlink_test.g.dart';\n\n@collection\nclass SourceModel {\n  SourceModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final link = IsarLink<TargetModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is SourceModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'SourceModel{id: $id, name: $name}';\n  }\n}\n\n@collection\nclass TargetModel {\n  Id id = Isar.autoIncrement;\n\n  @Backlink(to: 'link')\n  final backlink = IsarLink<SourceModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is TargetModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id;\n\n  @override\n  String toString() {\n    return 'TargetModel{id: $id}';\n  }\n}\n\nvoid main() {\n  group('Filter backlink', () {\n    late Isar isar;\n\n    late SourceModel source1;\n    late SourceModel source2;\n    late SourceModel source3;\n    late SourceModel source4;\n    late SourceModel source5;\n    late SourceModel source6;\n\n    late TargetModel target1;\n    late TargetModel target2;\n    late TargetModel target3;\n    late TargetModel target4;\n    late TargetModel target5;\n    late TargetModel target6;\n\n    setUp(() async {\n      isar = await openTempIsar([SourceModelSchema, TargetModelSchema]);\n\n      source1 = SourceModel('source 1');\n      source2 = SourceModel('source 2');\n      source3 = SourceModel('source 3');\n      source4 = SourceModel('source 4');\n      source5 = SourceModel('source 5');\n      source6 = SourceModel('source 6');\n\n      target1 = TargetModel();\n      target2 = TargetModel();\n      target3 = TargetModel();\n      target4 = TargetModel();\n      target5 = TargetModel();\n      target6 = TargetModel();\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.sourceModels.tPutAll([\n            source1,\n            source2,\n            source3,\n            source4,\n            source5,\n            source6,\n          ]),\n          isar.targetModels.tPutAll([\n            target1,\n            target2,\n            target3,\n            target4,\n            target5,\n            target6,\n          ]),\n        ]),\n      );\n\n      source1.link.value = target1;\n      source2.link.value = target2;\n      source3.link.value = target3;\n      source4.link.value = target4;\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          source1.link.tSave(),\n          source2.link.tSave(),\n          source3.link.tSave(),\n          source4.link.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.backlink()', () async {\n      await qEqualSet(\n        isar.targetModels.filter().backlink((q) => q.nameEqualTo('source 1')),\n        [target1],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlink((q) => q.nameEqualTo('source 2')),\n        [target2],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlink((q) => q.nameEqualTo('source 3')),\n        [target3],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlink((q) => q.nameEqualTo('source 4')),\n        [target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlink((q) => q.nameEqualTo('source 5')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlink((q) => q.nameEqualTo('source 6')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels\n            .filter()\n            .backlink((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels\n            .filter()\n            .backlink((q) => q.nameEqualTo('source 1'))\n            .or()\n            .backlink((q) => q.nameEqualTo('source 4')),\n        [target1, target4],\n      );\n    });\n\n    isarTest('.backlinkIsNull()', () async {\n      await qEqualSet(\n        isar.targetModels.filter().backlinkIsNull(),\n        [target5, target6],\n      );\n\n      await isar.tWriteTxn(() => target1.backlink.tReset());\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinkIsNull(),\n        [target1, target5, target6],\n      );\n\n      source6.link.value = target6;\n      await isar.tWriteTxn(() => source6.link.tSave());\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinkIsNull(),\n        [target1, target5],\n      );\n\n      await isar.tWriteTxn(() => isar.sourceModels.where().tDeleteAll());\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinkIsNull(),\n        [target1, target2, target3, target4, target5, target6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_backlinks_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_backlinks_test.g.dart';\n\n@collection\nclass SourceModel {\n  SourceModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final links = IsarLinks<TargetModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is SourceModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'SourceModel{id: $id, name: $name}';\n  }\n}\n\n@collection\nclass TargetModel {\n  Id id = Isar.autoIncrement;\n\n  @Backlink(to: 'links')\n  final backlinks = IsarLinks<SourceModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is TargetModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id;\n\n  @override\n  String toString() {\n    return 'TargetModel{id: $id}';\n  }\n}\n\nvoid main() {\n  group('Filter backlinks', () {\n    late Isar isar;\n\n    late SourceModel source1;\n    late SourceModel source2;\n    late SourceModel source3;\n    late SourceModel source4;\n    late SourceModel source5;\n    late SourceModel source6;\n\n    late TargetModel target1;\n    late TargetModel target2;\n    late TargetModel target3;\n    late TargetModel target4;\n    late TargetModel target5;\n    late TargetModel target6;\n\n    setUp(() async {\n      isar = await openTempIsar([SourceModelSchema, TargetModelSchema]);\n\n      source1 = SourceModel('source 1');\n      source2 = SourceModel('source 2');\n      source3 = SourceModel('source 3');\n      source4 = SourceModel('source 4');\n      source5 = SourceModel('source 5');\n      source6 = SourceModel('source 6');\n\n      target1 = TargetModel();\n      target2 = TargetModel();\n      target3 = TargetModel();\n      target4 = TargetModel();\n      target5 = TargetModel();\n      target6 = TargetModel();\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.sourceModels.tPutAll([\n            source1,\n            source2,\n            source3,\n            source4,\n            source5,\n            source6,\n          ]),\n          isar.targetModels.tPutAll([\n            target1,\n            target2,\n            target3,\n            target4,\n            target5,\n            target6,\n          ]),\n        ]),\n      );\n\n      source1.links.add(target1);\n      source2.links.addAll([target1, target2]);\n      source3.links.addAll([target1, target2, target3]);\n      source4.links.add(target4);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          source1.links.tSave(),\n          source2.links.tSave(),\n          source3.links.tSave(),\n          source4.links.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.backlinks()', () async {\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameStartsWith('source')),\n        [target1, target2, target3, target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameEqualTo('source 1')),\n        [target1],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameEqualTo('source 2')),\n        [target1, target2],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameEqualTo('source 3')),\n        [target1, target2, target3],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameEqualTo('source 4')),\n        [target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameEqualTo('source 5')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinks((q) => q.nameEqualTo('source 6')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels\n            .filter()\n            .backlinks((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels\n            .filter()\n            .backlinks(\n              (q) => q.nameEqualTo('source 1').or().nameEqualTo('source 2'),\n            )\n            .and()\n            .backlinks((q) => q.nameEqualTo('source 1')),\n        [target1],\n      );\n    });\n\n    isarTest('.backlinksLengthEqualTo()', () async {\n      expect(\n        () => isar.targetModels.filter().backlinksLengthEqualTo(-1),\n        throwsAssertionError,\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthEqualTo(0),\n        [target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthEqualTo(1),\n        [target3, target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthEqualTo(2),\n        [target2],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthEqualTo(3),\n        [target1],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthEqualTo(4),\n        [],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthEqualTo(5),\n        [],\n      );\n    });\n\n    isarTest('.backlinksLengthGreaterThan()', () async {\n      expect(\n        () => isar.targetModels.filter().backlinksLengthGreaterThan(-2),\n        throwsAssertionError,\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(0),\n        [target1, target2, target3, target4],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(0, include: true),\n        [target1, target2, target3, target4, target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(1),\n        [target1, target2],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(1, include: true),\n        [target1, target2, target3, target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(2),\n        [target1],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(2, include: true),\n        [target1, target2],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(3),\n        [],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthGreaterThan(3, include: true),\n        [target1],\n      );\n    });\n\n    isarTest('.backlinksLengthLessThan()', () async {\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(0),\n        [],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(0, include: true),\n        [target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(1),\n        [target5, target6],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(1, include: true),\n        [target3, target4, target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(2),\n        [target3, target4, target5, target6],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(2, include: true),\n        [target2, target3, target4, target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(3),\n        [target2, target3, target4, target5, target6],\n      );\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthLessThan(3, include: true),\n        [target1, target2, target3, target4, target5, target6],\n      );\n    });\n\n    isarTest('.backlinksLengthBetween()', () async {\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthBetween(0, 3),\n        [target1, target2, target3, target4, target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels\n            .filter()\n            .backlinksLengthBetween(0, 3, includeLower: false),\n        [target1, target2, target3, target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels\n            .filter()\n            .backlinksLengthBetween(0, 3, includeUpper: false),\n        [target2, target3, target4, target5, target6],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthBetween(\n              0,\n              3,\n              includeLower: false,\n              includeUpper: false,\n            ),\n        [target2, target3, target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthBetween(1, 2),\n        [target2, target3, target4],\n      );\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksLengthBetween(3, 42),\n        [target1],\n      );\n    });\n\n    isarTest('.backlinksIsEmpty()', () async {\n      await qEqualSet(\n        isar.targetModels.filter().backlinksIsEmpty(),\n        [target5, target6],\n      );\n\n      await isar.tWriteTxn(() => target1.backlinks.tReset());\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksIsEmpty(),\n        [target1, target5, target6],\n      );\n\n      await isar.tWriteTxn(() => isar.sourceModels.where().tDeleteAll());\n\n      await qEqualSet(\n        isar.targetModels.filter().backlinksIsEmpty(),\n        [target1, target2, target3, target4, target5, target6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_link_circular_direct_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_link_circular_direct_test.g.dart';\n\n@collection\nclass ModelA {\n  ModelA(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final bLinks = IsarLinks<ModelB>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is ModelA &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'ModelA{id: $id, name: $name}';\n  }\n}\n\n@collection\nclass ModelB {\n  ModelB(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final aLinks = IsarLinks<ModelA>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is ModelB &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'ModelB{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter link circular direct', () {\n    late Isar isar;\n\n    late ModelA a1;\n    late ModelA a2;\n    late ModelA a3;\n    late ModelA a4;\n    late ModelA a5;\n    late ModelA a6;\n\n    late ModelB b1;\n    late ModelB b2;\n    late ModelB b3;\n    late ModelB b4;\n    late ModelB b5;\n    late ModelB b6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelASchema, ModelBSchema]);\n\n      a1 = ModelA('a 1');\n      a2 = ModelA('a 2');\n      a3 = ModelA('a 3');\n      a4 = ModelA('a 4');\n      a5 = ModelA('a 5');\n      a6 = ModelA('a 6');\n\n      b1 = ModelB('b 1');\n      b2 = ModelB('b 2');\n      b3 = ModelB('b 3');\n      b4 = ModelB('b 4');\n      b5 = ModelB('b 5');\n      b6 = ModelB('b 6');\n\n      await isar.tWriteTxn(\n        () => Future.any([\n          isar.modelAs.tPutAll([a1, a2, a3, a4, a5, a6]),\n          isar.modelBs.tPutAll([b1, b2, b3, b4, b5, b6]),\n        ]),\n      );\n\n      a1.bLinks.add(b1);\n      a2.bLinks.addAll([b1, b2]);\n      a3.bLinks.addAll([b1, b2, b3]);\n      a4.bLinks.addAll([b3, b4]);\n      a5.bLinks.add(b5);\n\n      b1.aLinks.add(a1);\n      b2.aLinks.addAll([a1, a2]);\n      b3.aLinks.addAll([a1, a2, a3]);\n      b4.aLinks.addAll([a3, a4]);\n      b6.aLinks.add(a6);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          a1.bLinks.tSave(),\n          a2.bLinks.tSave(),\n          a3.bLinks.tSave(),\n          a4.bLinks.tSave(),\n          a5.bLinks.tSave(),\n          b1.aLinks.tSave(),\n          b2.aLinks.tSave(),\n          b3.aLinks.tSave(),\n          b4.aLinks.tSave(),\n          b6.aLinks.tSave(),\n        ]),\n      );\n    });\n\n    group('From ModelA', () {\n      isarTest('.bLinks()', () async {\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b')),\n          [a1, a2, a3, a4, a5],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b 1')),\n          [a1, a2, a3],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b 2')),\n          [a2, a3],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b 3')),\n          [a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b 4')),\n          [a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b 5')),\n          [a5],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('b 6')),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks((q) => q.nameStartsWith('non existing')),\n          [],\n        );\n      });\n\n      isarTest('.bLinks() then .aLinks()', () async {\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a'))),\n          [a1, a2, a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a 1'))),\n          [a1, a2, a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a 2'))),\n          [a2, a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a 3'))),\n          [a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a 4'))),\n          [a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a 5'))),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('a 6'))),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelAs\n              .filter()\n              .bLinks((q) => q.aLinks((q) => q.nameStartsWith('non existing'))),\n          [],\n        );\n      });\n\n      isarTest('.bLinks() then .aLinks() then .bLinks()', () async {\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b'),\n                  ),\n                ),\n              ),\n          [a1, a2, a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b 1'),\n                  ),\n                ),\n              ),\n          [a1, a2, a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b 2'),\n                  ),\n                ),\n              ),\n          [a2, a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b 3'),\n                  ),\n                ),\n              ),\n          [a3, a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b 4'),\n                  ),\n                ),\n              ),\n          [a4],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b 5'),\n                  ),\n                ),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('b 6'),\n                  ),\n                ),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelAs.filter().bLinks(\n                (q) => q.aLinks(\n                  (q) => q.bLinks(\n                    (q) => q.nameStartsWith('non existing'),\n                  ),\n                ),\n              ),\n          [],\n        );\n      });\n    });\n\n    group('From ModelB', () {\n      isarTest('.aLinks()', () async {\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a')),\n          [b1, b2, b3, b4, b6],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a 1')),\n          [b1, b2, b3],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a 2')),\n          [b2, b3],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a 3')),\n          [b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a 4')),\n          [b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a 5')),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('a 6')),\n          [b6],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks((q) => q.nameStartsWith('non existing')),\n          [],\n        );\n      });\n\n      isarTest('.aLinks() then .bLinks()', () async {\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameStartsWith('b'))),\n          [b1, b2, b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('b 1'))),\n          [b1, b2, b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('b 2'))),\n          [b2, b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('b 3'))),\n          [b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('b 4'))),\n          [b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('b 5'))),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('b 6'))),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelBs\n              .filter()\n              .aLinks((q) => q.bLinks((q) => q.nameEqualTo('non existing'))),\n          [],\n        );\n      });\n\n      isarTest('.aLinks() then .bLinks() then .aLinks()', () async {\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a'),\n                  ),\n                ),\n              ),\n          [b1, b2, b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a 1'),\n                  ),\n                ),\n              ),\n          [b1, b2, b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a 2'),\n                  ),\n                ),\n              ),\n          [b2, b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a 3'),\n                  ),\n                ),\n              ),\n          [b3, b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a 4'),\n                  ),\n                ),\n              ),\n          [b4],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a 5'),\n                  ),\n                ),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('a 6'),\n                  ),\n                ),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.modelBs.filter().aLinks(\n                (q) => q.bLinks(\n                  (q) => q.aLinks(\n                    (q) => q.nameStartsWith('non existing'),\n                  ),\n                ),\n              ),\n          [],\n        );\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_link_circular_indirect_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_link_circular_indirect_test.g.dart';\n\n@collection\nclass ModelA {\n  ModelA(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final bLinks = IsarLinks<ModelB>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is ModelA &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'ModelA{id: $id, name: $name}';\n  }\n}\n\n@collection\nclass ModelB {\n  ModelB(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final cLinks = IsarLinks<ModelC>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is ModelB &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'ModelB{id: $id, name: $name}';\n  }\n}\n\n@collection\nclass ModelC {\n  ModelC(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final aLinks = IsarLinks<ModelA>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is ModelC &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'ModelC{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter link circular indirect', () {\n    late Isar isar;\n\n    late ModelA a1;\n    late ModelA a2;\n    late ModelA a3;\n    late ModelA a4;\n    late ModelA a5;\n    late ModelA a6;\n\n    late ModelB b1;\n    late ModelB b2;\n    late ModelB b3;\n    late ModelB b4;\n    late ModelB b5;\n    late ModelB b6;\n\n    late ModelC c1;\n    late ModelC c2;\n    late ModelC c3;\n    late ModelC c4;\n    late ModelC c5;\n    late ModelC c6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelASchema, ModelBSchema, ModelCSchema]);\n\n      a1 = ModelA('a 1');\n      a2 = ModelA('a 2');\n      a3 = ModelA('a 3');\n      a4 = ModelA('a 4');\n      a5 = ModelA('a 5');\n      a6 = ModelA('a 6');\n\n      b1 = ModelB('b 1');\n      b2 = ModelB('b 2');\n      b3 = ModelB('b 3');\n      b4 = ModelB('b 4');\n      b5 = ModelB('b 5');\n      b6 = ModelB('b 6');\n\n      c1 = ModelC('c 1');\n      c2 = ModelC('c 2');\n      c3 = ModelC('c 3');\n      c4 = ModelC('c 4');\n      c5 = ModelC('c 5');\n      c6 = ModelC('c 6');\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.modelAs.tPutAll([a1, a2, a3, a4, a5, a6]),\n          isar.modelBs.tPutAll([b1, b2, b3, b4, b5, b6]),\n          isar.modelCs.tPutAll([c1, c2, c3, c4, c5, c6]),\n        ]),\n      );\n\n      a1.bLinks.add(b1);\n      a2.bLinks.addAll([b1, b2]);\n      a3.bLinks.addAll([b1, b2, b3]);\n      a4.bLinks.addAll([b3, b4]);\n      a5.bLinks.add(b5);\n\n      b1.cLinks.add(c1);\n      b2.cLinks.addAll([c1, c2]);\n      b3.cLinks.addAll([c1, c2, c3]);\n      b4.cLinks.addAll([c3, c4]);\n      b6.cLinks.add(c6);\n\n      c1.aLinks.add(a1);\n      c2.aLinks.addAll([a1, a2]);\n      c3.aLinks.addAll([a1, a2, a3]);\n      c4.aLinks.addAll([a3, a4]);\n      c5.aLinks.add(a6);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          a1.bLinks.tSave(),\n          a2.bLinks.tSave(),\n          a3.bLinks.tSave(),\n          a4.bLinks.tSave(),\n          a5.bLinks.tSave(),\n          b1.cLinks.tSave(),\n          b2.cLinks.tSave(),\n          b3.cLinks.tSave(),\n          b4.cLinks.tSave(),\n          b6.cLinks.tSave(),\n          c1.aLinks.tSave(),\n          c2.aLinks.tSave(),\n          c3.aLinks.tSave(),\n          c4.aLinks.tSave(),\n          c5.aLinks.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.bLinks() then .cLinks() then .aLinks()', () async {\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameStartsWith('a'),\n                ),\n              ),\n            ),\n        [a1, a2, a3, a4],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('a 1'),\n                ),\n              ),\n            ),\n        [a1, a2, a3, a4],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('a 2'),\n                ),\n              ),\n            ),\n        [a2, a3, a4],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('a 3'),\n                ),\n              ),\n            ),\n        [a3, a4],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('a 4'),\n                ),\n              ),\n            ),\n        [a4],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('a 5'),\n                ),\n              ),\n            ),\n        [],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('a 6'),\n                ),\n              ),\n            ),\n        [],\n      );\n\n      await qEqualSet(\n        isar.modelAs.filter().bLinks(\n              (q) => q.cLinks(\n                (q) => q.aLinks(\n                  (q) => q.nameEqualTo('non existing'),\n                ),\n              ),\n            ),\n        [],\n      );\n    });\n\n    isarTest('.cLinks() then .aLinks() then .bLinks()', () async {\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameStartsWith('b'),\n                ),\n              ),\n            ),\n        [b1, b2, b3, b4],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('b 1'),\n                ),\n              ),\n            ),\n        [b1, b2, b3, b4],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('b 2'),\n                ),\n              ),\n            ),\n        [b2, b3, b4],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('b 3'),\n                ),\n              ),\n            ),\n        [b3, b4],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('b 4'),\n                ),\n              ),\n            ),\n        [b4],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('b 5'),\n                ),\n              ),\n            ),\n        [],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('b 6'),\n                ),\n              ),\n            ),\n        [],\n      );\n\n      await qEqualSet(\n        isar.modelBs.filter().cLinks(\n              (q) => q.aLinks(\n                (q) => q.bLinks(\n                  (q) => q.nameEqualTo('non existing'),\n                ),\n              ),\n            ),\n        [],\n      );\n    });\n\n    isarTest('.aLinks() then .bLinks() then .cLinks()', () async {\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameStartsWith('c'),\n                ),\n              ),\n            ),\n        [c1, c2, c3, c4],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('c 1'),\n                ),\n              ),\n            ),\n        [c1, c2, c3, c4],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('c 2'),\n                ),\n              ),\n            ),\n        [c2, c3, c4],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('c 3'),\n                ),\n              ),\n            ),\n        [c3, c4],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('c 4'),\n                ),\n              ),\n            ),\n        [c4],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('c 5'),\n                ),\n              ),\n            ),\n        [],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('c 6'),\n                ),\n              ),\n            ),\n        [],\n      );\n\n      await qEqualSet(\n        isar.modelCs.filter().aLinks(\n              (q) => q.bLinks(\n                (q) => q.cLinks(\n                  (q) => q.nameEqualTo('non existing'),\n                ),\n              ),\n            ),\n        [],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_link_nested_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_link_nested_test.g.dart';\n\n@collection\nclass SourceModel {\n  SourceModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  final links = IsarLinks<TargetModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is SourceModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id;\n\n  @override\n  String toString() {\n    return 'SourceModel{id: $id}';\n  }\n}\n\n@collection\nclass TargetModel {\n  TargetModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  final String name;\n\n  final nestedLinks = IsarLinks<NestedTargetModel>();\n\n  @Backlink(to: 'links')\n  final linksBacklinks = IsarLinks<SourceModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is TargetModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'TargetModel{id: $id, name: $name}';\n  }\n}\n\n@collection\nclass NestedTargetModel {\n  NestedTargetModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  final String name;\n\n  @Backlink(to: 'nestedLinks')\n  final nestedLinksBacklinks = IsarLinks<TargetModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is NestedTargetModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'NestedTargetModel{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter link nested', () {\n    late Isar isar;\n\n    late SourceModel source1;\n    late SourceModel source2;\n    late SourceModel source3;\n    late SourceModel source4;\n    late SourceModel source5;\n    late SourceModel source6;\n\n    late TargetModel target1;\n    late TargetModel target2;\n    late TargetModel target3;\n    late TargetModel target4;\n    late TargetModel target5;\n    late TargetModel target6;\n\n    late NestedTargetModel nestedTarget1;\n    late NestedTargetModel nestedTarget2;\n    late NestedTargetModel nestedTarget3;\n    late NestedTargetModel nestedTarget4;\n    late NestedTargetModel nestedTarget5;\n    late NestedTargetModel nestedTarget6;\n\n    setUp(() async {\n      isar = await openTempIsar([\n        SourceModelSchema,\n        TargetModelSchema,\n        NestedTargetModelSchema,\n      ]);\n\n      source1 = SourceModel('source 1');\n      source2 = SourceModel('source 2');\n      source3 = SourceModel('source 3');\n      source4 = SourceModel('source 4');\n      source5 = SourceModel('source 5');\n      source6 = SourceModel('source 6');\n\n      target1 = TargetModel('target 1');\n      target2 = TargetModel('target 2');\n      target3 = TargetModel('target 3');\n      target4 = TargetModel('target 4');\n      target5 = TargetModel('target 5');\n      target6 = TargetModel('target 6');\n\n      nestedTarget1 = NestedTargetModel('nested target 1');\n      nestedTarget2 = NestedTargetModel('nested target 2');\n      nestedTarget3 = NestedTargetModel('nested target 3');\n      nestedTarget4 = NestedTargetModel('nested target 4');\n      nestedTarget5 = NestedTargetModel('nested target 5');\n      nestedTarget6 = NestedTargetModel('nested target 6');\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.sourceModels.tPutAll([\n            source1,\n            source2,\n            source3,\n            source4,\n            source5,\n            source6,\n          ]),\n          isar.targetModels.tPutAll([\n            target1,\n            target2,\n            target3,\n            target4,\n            target5,\n            target6,\n          ]),\n          isar.nestedTargetModels.tPutAll([\n            nestedTarget1,\n            nestedTarget2,\n            nestedTarget3,\n            nestedTarget4,\n            nestedTarget5,\n            nestedTarget6,\n          ]),\n        ]),\n      );\n\n      source1.links.add(target1);\n      source2.links.addAll([target1, target2]);\n      source3.links.addAll([target1, target2, target3]);\n      source4.links.addAll([target3, target4]);\n      source5.links.add(target5);\n\n      target1.nestedLinks.add(nestedTarget1);\n      target2.nestedLinks.addAll([nestedTarget1, nestedTarget2]);\n      target3.nestedLinks.addAll([nestedTarget1, nestedTarget2, nestedTarget3]);\n      target4.nestedLinks.addAll([nestedTarget3, nestedTarget4, nestedTarget5]);\n      target6.nestedLinks.add(nestedTarget5);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          source1.links.tSave(),\n          source2.links.tSave(),\n          source3.links.tSave(),\n          source4.links.tSave(),\n          source5.links.tSave(),\n          target1.nestedLinks.tSave(),\n          target2.nestedLinks.tSave(),\n          target3.nestedLinks.tSave(),\n          target4.nestedLinks.tSave(),\n          target6.nestedLinks.tSave(),\n        ]),\n      );\n    });\n\n    group('Links', () {\n      isarTest('.links()', () async {\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nameStartsWith('target')),\n          [source1, source2, source3, source4, source5],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nameEqualTo('target 1')),\n          [source1, source2, source3],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nameEqualTo('target 2')),\n          [source2, source3],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nameEqualTo('target 3')),\n          [source3, source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nameEqualTo('target 4')),\n          [source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nameEqualTo('target 5')),\n          [source5],\n        );\n\n        await qEqualSet(\n          isar.sourceModels\n              .filter()\n              .links((q) => q.nameEqualTo('non existing')),\n          [],\n        );\n      });\n\n      isarTest('.links() with .nestedLinks()', () async {\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameStartsWith('nested target')),\n              ),\n          [source1, source2, source3, source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('nested target 1')),\n              ),\n          [source1, source2, source3, source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('nested target 2')),\n              ),\n          [source2, source3, source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('nested target 3')),\n              ),\n          [source3, source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('nested target 4')),\n              ),\n          [source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('nested target 5')),\n              ),\n          [source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('nested target 6')),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links(\n                (q) => q.nestedLinks((q) => q.nameEqualTo('non existing')),\n              ),\n          [],\n        );\n      });\n\n      isarTest('.links() with .nestedLinksLengthEqualTo()', () async {\n        await qEqualSet(\n          isar.sourceModels\n              .filter()\n              .links((q) => q.nestedLinksLengthEqualTo(0)),\n          [source5],\n        );\n\n        await qEqualSet(\n          isar.sourceModels\n              .filter()\n              .links((q) => q.nestedLinksLengthEqualTo(1)),\n          [source1, source2, source3],\n        );\n\n        await qEqualSet(\n          isar.sourceModels\n              .filter()\n              .links((q) => q.nestedLinksLengthEqualTo(2)),\n          [source2, source3],\n        );\n\n        await qEqualSet(\n          isar.sourceModels\n              .filter()\n              .links((q) => q.nestedLinksLengthEqualTo(3)),\n          [source3, source4],\n        );\n\n        await qEqualSet(\n          isar.sourceModels\n              .filter()\n              .links((q) => q.nestedLinksLengthEqualTo(4)),\n          [],\n        );\n      });\n\n      isarTest('.links() with .nestedLinkIsEmpty()', () async {\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nestedLinksIsEmpty()),\n          [source5],\n        );\n\n        await isar.tWriteTxn(() => target1.nestedLinks.tReset());\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nestedLinksIsEmpty()),\n          [source1, source2, source3, source5],\n        );\n\n        await isar.tWriteTxn(\n          () => isar.nestedTargetModels.where().tDeleteAll(),\n        );\n\n        await qEqualSet(\n          isar.sourceModels.filter().links((q) => q.nestedLinksIsEmpty()),\n          [source1, source2, source3, source4, source5],\n        );\n      });\n    });\n\n    group('Backlinks', () {\n      isarTest('.nestedLinksBacklinks()', () async {\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target')),\n          [\n            nestedTarget1,\n            nestedTarget2,\n            nestedTarget3,\n            nestedTarget4,\n            nestedTarget5,\n          ],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target 1')),\n          [nestedTarget1],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target 2')),\n          [nestedTarget1, nestedTarget2],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target 3')),\n          [nestedTarget1, nestedTarget2, nestedTarget3],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target 4')),\n          [nestedTarget3, nestedTarget4, nestedTarget5],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target 5')),\n          [],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('target 6')),\n          [nestedTarget5],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.nameStartsWith('non existing')),\n          [],\n        );\n      });\n\n      isarTest('.nestedLinksBackLinks', () async {\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameStartsWith('source')),\n              ),\n          [\n            nestedTarget1,\n            nestedTarget2,\n            nestedTarget3,\n            nestedTarget4,\n            nestedTarget5,\n          ],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('source 1')),\n              ),\n          [nestedTarget1],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('source 2')),\n              ),\n          [nestedTarget1, nestedTarget2],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('source 3')),\n              ),\n          [nestedTarget1, nestedTarget2, nestedTarget3],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('source 4')),\n              ),\n          [\n            nestedTarget1,\n            nestedTarget2,\n            nestedTarget3,\n            nestedTarget4,\n            nestedTarget5,\n          ],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('source 5')),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('source 6')),\n              ),\n          [],\n        );\n\n        await qEqualSet(\n          isar.nestedTargetModels.filter().nestedLinksBacklinks(\n                (q) => q.linksBacklinks((q) => q.nameEqualTo('non existing')),\n              ),\n          [],\n        );\n      });\n\n      isarTest(\n        '.nestedLinksBacklinks() with .linksBacklinksLengthEqualTo()',\n        () async {\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(0)),\n            [nestedTarget5],\n          );\n\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(1)),\n            [nestedTarget3, nestedTarget4, nestedTarget5],\n          );\n\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(2)),\n            [nestedTarget1, nestedTarget2, nestedTarget3],\n          );\n\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(3)),\n            [nestedTarget1],\n          );\n\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(4)),\n            [],\n          );\n\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(5)),\n            [],\n          );\n\n          await qEqualSet(\n            isar.nestedTargetModels\n                .filter()\n                .nestedLinksBacklinks((q) => q.linksBacklinksLengthEqualTo(6)),\n            [],\n          );\n        },\n      );\n    });\n\n    isarTest(\n      '.nestedLinksBacklinks() with .linksBacklinksIsEmpty()',\n      () async {\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.linksBacklinksIsEmpty()),\n          [nestedTarget5],\n        );\n\n        await isar.tWriteTxn(() => target1.linksBacklinks.tReset());\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.linksBacklinksIsEmpty()),\n          [nestedTarget1, nestedTarget5],\n        );\n\n        await isar.tWriteTxn(() => isar.sourceModels.where().tDeleteAll());\n\n        await qEqualSet(\n          isar.nestedTargetModels\n              .filter()\n              .nestedLinksBacklinks((q) => q.linksBacklinksIsEmpty()),\n          [\n            nestedTarget1,\n            nestedTarget2,\n            nestedTarget3,\n            nestedTarget4,\n            nestedTarget5,\n          ],\n        );\n      },\n    );\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_link_self_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_link_self_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  final String name;\n\n  final selfLink = IsarLink<Model>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'Model{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter self link', () {\n    late Isar isar;\n\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n    late Model obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      obj1 = Model('obj 1');\n      obj2 = Model('obj 2');\n      obj3 = Model('obj 3');\n      obj4 = Model('obj 4');\n      obj5 = Model('obj 5');\n      obj6 = Model('obj 6');\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n\n      obj1.selfLink.value = obj2;\n      obj2.selfLink.value = obj3;\n      obj3.selfLink.value = obj4;\n      obj4.selfLink.value = obj5;\n      obj5.selfLink.value = obj1;\n      obj6.selfLink.value = obj6;\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          obj1.selfLink.tSave(),\n          obj2.selfLink.tSave(),\n          obj3.selfLink.tSave(),\n          obj4.selfLink.tSave(),\n          obj5.selfLink.tSave(),\n          obj6.selfLink.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.selfLink()', () async {\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameStartsWith('obj')),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('obj 1')),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('obj 2')),\n        [obj1],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('obj 3')),\n        [obj2],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('obj 4')),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('obj 5')),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('obj 6')),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n    });\n\n    isarTest('Nested .selfLink()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj'))),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj 1'))),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj 2'))),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj 3'))),\n        [obj1],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj 4'))),\n        [obj2],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj 5'))),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLink((q) => q.selfLink((q) => q.nameStartsWith('obj 6'))),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLink(\n              (q) => q.selfLink((q) => q.nameStartsWith('non existing')),\n            ),\n        [],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_link_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_link_test.g.dart';\n\n@collection\nclass SourceModel {\n  Id id = Isar.autoIncrement;\n\n  final link = IsarLink<TargetModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is SourceModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id;\n\n  @override\n  String toString() {\n    return 'SourceModel{id: $id, link: $link}';\n  }\n}\n\n@collection\nclass TargetModel {\n  TargetModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is TargetModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'TargetModel{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter link', () {\n    late Isar isar;\n\n    late SourceModel source1;\n    late SourceModel source2;\n    late SourceModel source3;\n    late SourceModel source4;\n    late SourceModel source5;\n    late SourceModel source6;\n\n    late TargetModel target1;\n    late TargetModel target2;\n    late TargetModel target3;\n    late TargetModel target4;\n    late TargetModel target5;\n    late TargetModel target6;\n\n    setUp(() async {\n      isar = await openTempIsar([SourceModelSchema, TargetModelSchema]);\n\n      source1 = SourceModel();\n      source2 = SourceModel();\n      source3 = SourceModel();\n      source4 = SourceModel();\n      source5 = SourceModel();\n      source6 = SourceModel();\n\n      target1 = TargetModel('target 1');\n      target2 = TargetModel('target 2');\n      target3 = TargetModel('target 3');\n      target4 = TargetModel('target 4');\n      target5 = TargetModel('target 5');\n      target6 = TargetModel('target 6');\n\n      await isar.tWriteTxn(\n        () => Future.any([\n          isar.sourceModels.tPutAll([\n            source1,\n            source2,\n            source3,\n            source4,\n            source5,\n            source6,\n          ]),\n          isar.targetModels.tPutAll([\n            target1,\n            target2,\n            target3,\n            target4,\n            target5,\n            target6,\n          ]),\n        ]),\n      );\n\n      source1.link.value = target1;\n      source2.link.value = target2;\n      source3.link.value = target3;\n      source4.link.value = target1;\n\n      await isar.tWriteTxn(\n        () => Future.value([\n          source1.link.tSave(),\n          source2.link.tSave(),\n          source3.link.tSave(),\n          source4.link.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.link()', () async {\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameContains('target')),\n        [source1, source2, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('target 1')),\n        [source1, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('target 2')),\n        [source2],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('target 3')),\n        [source3],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('target 4')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('target 5')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('target 6')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().link((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .link((q) => q.nameEqualTo('target 1'))\n            .or()\n            .link((q) => q.nameEqualTo('target 6')),\n        [source1, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .link((q) => q.nameEqualTo('target 3').and().nameContains('3')),\n        [source3],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .link((q) => q.nameEqualTo('target 1'))\n            .limit(1),\n        [source1],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqualSet(\n        isar.sourceModels.filter().linkIsNull(),\n        [source5, source6],\n      );\n\n      await isar.tWriteTxn(() => source1.link.tReset());\n\n      await qEqualSet(\n        isar.sourceModels.filter().linkIsNull(),\n        [source1, source5, source6],\n      );\n\n      source6.link.value = target6;\n      await isar.tWriteTxn(() => source6.link.tSave());\n\n      await qEqualSet(\n        isar.sourceModels.filter().linkIsNull(),\n        [source1, source5],\n      );\n\n      await isar.tWriteTxn(() => isar.targetModels.where().tDeleteAll());\n\n      await qEqualSet(\n        isar.sourceModels.filter().linkIsNull(),\n        [source1, source2, source3, source4, source5, source6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_links_self_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_links_self_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  final String name;\n\n  final selfLinks = IsarLinks<Model>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Model &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'Model{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter self links', () {\n    late Isar isar;\n\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n    late Model obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      obj1 = Model('obj 1');\n      obj2 = Model('obj 2');\n      obj3 = Model('obj 3');\n      obj4 = Model('obj 4');\n      obj5 = Model('obj 5');\n      obj6 = Model('obj 6');\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n\n      obj1.selfLinks.add(obj1);\n      obj2.selfLinks.addAll([obj1, obj2]);\n      obj3.selfLinks.addAll([obj1, obj2, obj3]);\n      obj4.selfLinks.addAll([obj4, obj5, obj1]);\n      obj5.selfLinks.add(obj6);\n      obj6.selfLinks.addAll([obj2, obj6]);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          obj1.selfLinks.tSave(),\n          obj2.selfLinks.tSave(),\n          obj3.selfLinks.tSave(),\n          obj4.selfLinks.tSave(),\n          obj5.selfLinks.tSave(),\n          obj6.selfLinks.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.selfLinks()', () async {\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameStartsWith('obj')),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('obj 1')),\n        [obj1, obj2, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('obj 2')),\n        [obj2, obj3, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('obj 3')),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('obj 4')),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('obj 5')),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('obj 6')),\n        [obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n    });\n\n    isarTest('Nested .selfLinks()', () async {\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameStartsWith('obj'))),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameEqualTo('obj 1'))),\n        [obj1, obj2, obj3, obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameEqualTo('obj 2'))),\n        [obj2, obj3, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameEqualTo('obj 3'))),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameEqualTo('obj 4'))),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameEqualTo('obj 5'))),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.models\n            .filter()\n            .selfLinks((q) => q.selfLinks((q) => q.nameEqualTo('obj 6'))),\n        [obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.models.filter().selfLinks(\n              (q) => q.selfLinks((q) => q.nameEqualTo('non existing')),\n            ),\n        [],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/filter/link/filter_links_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'filter_links_test.g.dart';\n\n@collection\nclass SourceModel {\n  Id id = Isar.autoIncrement;\n\n  final links = IsarLinks<TargetModel>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is SourceModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id;\n\n  @override\n  String toString() {\n    return 'SourceModel{id: $id, links: $links}';\n  }\n}\n\n@collection\nclass TargetModel {\n  TargetModel(this.name);\n\n  Id id = Isar.autoIncrement;\n\n  String name;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is TargetModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          name == other.name;\n\n  @override\n  String toString() {\n    return 'TargetModel{id: $id, name: $name}';\n  }\n}\n\nvoid main() {\n  group('Filter links', () {\n    late Isar isar;\n\n    late SourceModel source1;\n    late SourceModel source2;\n    late SourceModel source3;\n    late SourceModel source4;\n    late SourceModel source5;\n    late SourceModel source6;\n\n    late TargetModel target1;\n    late TargetModel target2;\n    late TargetModel target3;\n    late TargetModel target4;\n    late TargetModel target5;\n    late TargetModel target6;\n\n    setUp(() async {\n      isar = await openTempIsar([SourceModelSchema, TargetModelSchema]);\n\n      source1 = SourceModel();\n      source2 = SourceModel();\n      source3 = SourceModel();\n      source4 = SourceModel();\n      source5 = SourceModel();\n      source6 = SourceModel();\n\n      target1 = TargetModel('target 1');\n      target2 = TargetModel('target 2');\n      target3 = TargetModel('target 3');\n      target4 = TargetModel('target 4');\n      target5 = TargetModel('target 5');\n      target6 = TargetModel('target 6');\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          isar.sourceModels.tPutAll([\n            source1,\n            source2,\n            source3,\n            source4,\n            source5,\n            source6,\n          ]),\n          isar.targetModels.tPutAll([\n            target1,\n            target2,\n            target3,\n            target4,\n            target5,\n            target6,\n          ]),\n        ]),\n      );\n\n      source1.links.add(target1);\n      source2.links.addAll([target1, target2, target3]);\n      source3.links.add(target2);\n      source4.links.addAll([target4, target2]);\n\n      await isar.tWriteTxn(\n        () => Future.wait([\n          source1.links.tSave(),\n          source2.links.tSave(),\n          source3.links.tSave(),\n          source4.links.tSave(),\n        ]),\n      );\n    });\n\n    isarTest('.links()', () async {\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameStartsWith('target')),\n        [source1, source2, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('target 1')),\n        [source1, source2],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('target 2')),\n        [source2, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('target 3')),\n        [source2],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('target 4')),\n        [source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('target 5')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('target 6')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().links((q) => q.nameEqualTo('non existing')),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .links(\n              (q) => q.nameEqualTo('target 1').or().nameEqualTo('target 2'),\n            )\n            .and()\n            .links((q) => q.nameEqualTo('target 1')),\n        [source1, source2],\n      );\n    });\n\n    isarTest('.linksLengthEqualTo()', () async {\n      expect(\n        () => isar.sourceModels.filter().linksLengthEqualTo(-1),\n        throwsAssertionError,\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthEqualTo(0),\n        [source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthEqualTo(1),\n        [source1, source3],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthEqualTo(2),\n        [source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthEqualTo(3),\n        [source2],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthEqualTo(4),\n        [],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthEqualTo(5),\n        [],\n      );\n    });\n\n    isarTest('.linksLengthGreaterThan()', () async {\n      expect(\n        () => isar.sourceModels.filter().linksLengthGreaterThan(-2),\n        throwsAssertionError,\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(0),\n        [source1, source2, source3, source4],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(0, include: true),\n        [source1, source2, source3, source4, source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(1),\n        [source2, source4],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(1, include: true),\n        [source1, source2, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(2),\n        [source2],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(2, include: true),\n        [source2, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(3),\n        [],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthGreaterThan(3, include: true),\n        [source2],\n      );\n    });\n\n    isarTest('.linksLengthLessThan()', () async {\n      expect(\n        () => isar.sourceModels.filter().linksLengthLessThan(-1),\n        throwsAssertionError,\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(0),\n        [],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(0, include: true),\n        [source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(1),\n        [source5, source6],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(1, include: true),\n        [source1, source3, source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(2),\n        [source1, source3, source5, source6],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(2, include: true),\n        [source1, source3, source4, source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(3),\n        [source1, source3, source4, source5, source6],\n      );\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(3, include: true),\n        [source1, source2, source3, source4, source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthLessThan(9223372036854775807),\n        [source1, source2, source3, source4, source5, source6],\n      );\n    });\n\n    isarTest('.linksLengthBetween()', () async {\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthBetween(0, 3),\n        [source1, source2, source3, source4, source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .linksLengthBetween(0, 3, includeLower: false),\n        [source1, source2, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .linksLengthBetween(0, 3, includeUpper: false),\n        [source1, source3, source4, source5, source6],\n      );\n\n      await qEqualSet(\n        isar.sourceModels\n            .filter()\n            .linksLengthBetween(0, 3, includeLower: false, includeUpper: false),\n        [source1, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthBetween(1, 2),\n        [source1, source3, source4],\n      );\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksLengthBetween(3, 42),\n        [source2],\n      );\n    });\n\n    isarTest('.linksIsEmpty', () async {\n      await qEqualSet(\n        isar.sourceModels.filter().linksIsEmpty(),\n        [source5, source6],\n      );\n\n      await isar.tWriteTxn(() => source1.links.tReset());\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksIsEmpty(),\n        [source1, source5, source6],\n      );\n\n      await isar.tWriteTxn(() => isar.targetModels.where().tDeleteAll());\n\n      await qEqualSet(\n        isar.sourceModels.filter().linksIsEmpty(),\n        [source1, source2, source3, source4, source5, source6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/id_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'id_test.g.dart';\n\n@collection\nclass ImplicitNullableIdModel {\n  Id? id;\n}\n\n@collection\nclass ImplicitFinalIdModel {\n  final Id id = Isar.autoIncrement;\n}\n\n@collection\nclass ExplicitIdModel {\n  Id? id;\n}\n\nvoid main() {\n  group('Id', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([\n        ImplicitNullableIdModelSchema,\n        ImplicitFinalIdModelSchema,\n        ExplicitIdModelSchema,\n      ]);\n    });\n\n    group('Implicit nullable id', () {\n      isarTest('Id should auto increment', () async {\n        final id1 = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n        );\n        expect(id1, 1);\n\n        final model = ImplicitNullableIdModel()..id = 2;\n        final id2 = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPut(model),\n        );\n        expect(id2, 2);\n\n        final id3 = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n        );\n        expect(id3, 3);\n\n        final ids = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPutAll(\n            List.generate(6, (_) => ImplicitNullableIdModel()),\n          ),\n        );\n        expect(ids, [4, 5, 6, 7, 8, 9]);\n\n        await qEqual(\n          isar.implicitNullableIdModels.where().idProperty(),\n          [1, 2, 3, 4, 5, 6, 7, 8, 9],\n        );\n      });\n\n      isarTest('Auto increment should reset', () async {\n        final ids1 = await isar.tWriteTxn(() {\n          return isar.implicitNullableIdModels.tPutAll(\n            List.generate(10, (_) => ImplicitNullableIdModel()),\n          );\n        });\n        expect(ids1, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);\n\n        final id11 = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n        );\n        expect(id11, 11);\n\n        await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tClear(),\n        );\n\n        final id1 = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n        );\n        expect(id1, 1);\n\n        final newIds = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPutAll(\n            List.generate(5, (index) => ImplicitNullableIdModel()),\n          ),\n        );\n        expect(newIds, [2, 3, 4, 5, 6]);\n\n        await qEqual(\n          isar.implicitNullableIdModels.where().idProperty(),\n          [1, 2, 3, 4, 5, 6],\n        );\n      });\n\n      isarTest('Negative id', () async {\n        final model = ImplicitNullableIdModel()..id = -10;\n        final id = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPut(model),\n        );\n        expect(id, -10);\n\n        final newModel = await isar.implicitNullableIdModels.tGet(id);\n        expect(newModel?.id, id);\n\n        final ids = await isar.tWriteTxn(\n          () => isar.implicitNullableIdModels.tPutAll(\n            List.generate(16, (_) => ImplicitNullableIdModel()),\n          ),\n        );\n        // Auto increment ids are always positive (minimum 1)\n        expect(ids, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n      });\n\n      isarTest(\n        'Auto increment counter should always be the next biggest id',\n        () async {\n          final id1 = await isar.tWriteTxn(\n            () => isar.implicitNullableIdModels.tPut(\n              ImplicitNullableIdModel()..id = 1024,\n            ),\n          );\n          expect(id1, 1024);\n\n          final autoGeneratedId1 = await isar.tWriteTxn(\n            () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n          );\n          expect(autoGeneratedId1, 1025);\n\n          final id2 = await isar.tWriteTxn(\n            () => isar.implicitNullableIdModels.tPut(\n              ImplicitNullableIdModel()..id = 4096,\n            ),\n          );\n          expect(id2, 4096);\n\n          final autoGeneratedId2 = await isar.tWriteTxn(\n            () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n          );\n          expect(autoGeneratedId2, 4097);\n\n          final deleted = await isar.tWriteTxn(\n            () => isar.implicitNullableIdModels.tDelete(4097),\n          );\n          expect(deleted, true);\n\n          final autoGeneratedId3 = await isar.tWriteTxn(\n            () => isar.implicitNullableIdModels.tPut(ImplicitNullableIdModel()),\n          );\n          expect(autoGeneratedId3, 4098);\n        },\n      );\n    });\n\n    group('Implicit final id', () {\n      isarTest('Id should auto increment', () async {\n        final id1 = await isar.tWriteTxn(\n          () => isar.implicitFinalIdModels.tPut(ImplicitFinalIdModel()),\n        );\n        expect(id1, 1);\n\n        final id2 = await isar.tWriteTxn(\n          () => isar.implicitFinalIdModels.tPut(ImplicitFinalIdModel()),\n        );\n        expect(id2, 2);\n\n        final ids = await isar.tWriteTxn(\n          () => isar.implicitFinalIdModels.tPutAll(\n            List.generate(6, (_) => ImplicitFinalIdModel()),\n          ),\n        );\n        expect(ids, [3, 4, 5, 6, 7, 8]);\n\n        await qEqual(\n          isar.implicitFinalIdModels.where().idProperty(),\n          [1, 2, 3, 4, 5, 6, 7, 8],\n        );\n      });\n    });\n\n    group('Explicit id', () {\n      isarTest('Id should auto increment', () async {\n        final id1 = await isar.tWriteTxn(\n          () => isar.explicitIdModels.tPut(ExplicitIdModel()),\n        );\n        expect(id1, 1);\n\n        final model = ExplicitIdModel()..id = 2;\n        final id2 = await isar.tWriteTxn(\n          () => isar.explicitIdModels.tPut(model),\n        );\n        expect(id2, 2);\n\n        final id3 = await isar.tWriteTxn(\n          () => isar.explicitIdModels.tPut(ExplicitIdModel()),\n        );\n        expect(id3, 3);\n\n        final ids = await isar.tWriteTxn(\n          () => isar.explicitIdModels.tPutAll(\n            List.generate(6, (_) => ExplicitIdModel()),\n          ),\n        );\n        expect(ids, [4, 5, 6, 7, 8, 9]);\n\n        await qEqual(\n          isar.explicitIdModels.where().idProperty(),\n          [1, 2, 3, 4, 5, 6, 7, 8, 9],\n        );\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/composite2_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'composite2_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.value1, this.value2);\n  Id? id;\n\n  @Index(\n    composite: [CompositeIndex('value2')],\n    unique: true,\n  )\n  int value1;\n\n  double value2;\n\n  @override\n  String toString() {\n    return '{id: $id, value1: $value1, value2: $value2}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return (other is Model) &&\n        other.id == id &&\n        other.value1 == value1 &&\n        other.value2 == value2;\n  }\n}\n\nvoid main() {\n  group('Composite Index2', () {\n    late Isar isar;\n    late IsarCollection<Model> col;\n\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n      col = isar.models;\n\n      obj1 = Model(1, 1);\n      obj2 = Model(1, 2);\n      obj3 = Model(2, 1);\n      obj4 = Model(2, 3);\n\n      await isar.writeTxn(() async {\n        await col.putAll([obj3, obj1, obj4, obj2]);\n      });\n    });\n\n    isarTest('sorted by value1 value2', () async {\n      await qEqual(\n        col.where().anyValue1Value2(),\n        [obj1, obj2, obj3, obj4],\n      );\n    });\n\n    isarTest('getBy value1 sorted by value2', () async {\n      await qEqual(\n        col.where().value1GreaterThanAnyValue2(0),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqual(col.where().value1EqualToAnyValue2(1), [obj1, obj2]);\n      await qEqual(col.where().value1EqualToAnyValue2(2), [obj3, obj4]);\n    });\n\n    isarTest('getBy value1 and value2', () async {\n      await qEqual(\n        col.where().value1EqualToValue2GreaterThan(1, 0),\n        [obj1, obj2],\n      );\n      await qEqual(\n        col.where().value1EqualToValue2GreaterThan(2, 0),\n        [obj3, obj4],\n      );\n      await qEqual(col.where().value1EqualToValue2GreaterThan(2, 1), [obj4]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/composite3_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'composite3_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.value1, this.value2, this.value3);\n  Id? id;\n\n  @Index(\n    composite: [\n      CompositeIndex('value2'),\n      CompositeIndex('value3'),\n    ],\n    unique: true,\n  )\n  int? value1;\n\n  int? value2;\n\n  int? value3;\n\n  @override\n  String toString() {\n    return '{id: $id, value1: $value1, value2: $value2, value3: $value3}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return (other is Model) &&\n        other.id == id &&\n        other.value1 == value1 &&\n        other.value2 == value2 &&\n        other.value3 == value3;\n  }\n}\n\nvoid main() {\n  group('Composite Index3', () {\n    late Isar isar;\n    late IsarCollection<Model> col;\n\n    late Model objNull1;\n    late Model objNull2;\n    late Model objNull3;\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n    late Model obj6;\n    late Model obj7;\n    late Model obj8;\n    late Model obj9;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n      col = isar.models;\n\n      objNull1 = Model(null, 10, 1);\n      objNull2 = Model(10, null, 1);\n      objNull3 = Model(10, 1, null);\n      obj1 = Model(100, 10, 1);\n      obj2 = Model(100, 10, 2);\n      obj3 = Model(100, 20, 1);\n      obj4 = Model(200, 10, 1);\n      obj5 = Model(200, 10, 2);\n      obj6 = Model(200, 20, 1);\n      obj7 = Model(300, 10, 1);\n      obj8 = Model(300, 10, 2);\n      obj9 = Model(300, 20, 1);\n\n      await isar.writeTxn(() async {\n        await col.putAll(\n          [\n            obj7, objNull3, obj4, obj1, objNull1, obj9, //\n            obj3, obj5, objNull2, obj2, obj8, obj6 //\n          ],\n        );\n      });\n    });\n\n    isarTest('.anyValue1Value2Value3', () async {\n      await qEqual(\n        isar.models.where().anyValue1Value2Value3(),\n        [\n          objNull1, objNull2, objNull3, obj1, obj2, //\n          obj3, obj4, obj5, obj6, obj7, obj8, obj9 //\n        ],\n      );\n      await qEqual(\n        isar.models.where(sort: Sort.desc).anyValue1Value2Value3(),\n        [\n          obj9, obj8, obj7, obj6, obj5, obj4, obj3, //\n          obj2, obj1, objNull3, objNull2, objNull1 //\n        ],\n      );\n    });\n\n    group('value1', () {\n      isarTest('.equalTo()', () async {\n        await qEqual(\n          isar.models.where().value1EqualToAnyValue2Value3(null),\n          [objNull1],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToAnyValue2Value3(200),\n          [obj4, obj5, obj6],\n        );\n      });\n\n      isarTest('.notEqualTo()', () async {\n        await qEqual(\n          isar.models.where().value1NotEqualToAnyValue2Value3(null),\n          [\n            objNull2, objNull3, obj1, obj2, //\n            obj3, obj4, obj5, obj6, obj7, obj8, obj9 //\n          ],\n        );\n        await qEqual(\n          isar.models.where().value1NotEqualToAnyValue2Value3(200),\n          [\n            objNull1, objNull2, objNull3, obj1, //\n            obj2, obj3, obj7, obj8, obj9 //\n          ],\n        );\n        await qEqual(\n          isar.models\n              .where(sort: Sort.desc)\n              .value1NotEqualToAnyValue2Value3(200),\n          [\n            obj9, obj8, obj7, obj3, obj2, //\n            obj1, objNull3, objNull2, objNull1, //\n          ],\n        );\n      });\n\n      isarTest('.greaterThan()', () async {\n        await qEqual(\n          isar.models.where().value1GreaterThanAnyValue2Value3(null),\n          [\n            objNull2, objNull3, obj1, obj2, obj3, //\n            obj4, obj5, obj6, obj7, obj8, obj9 //\n          ],\n        );\n        await qEqual(\n          isar.models.where().value1GreaterThanAnyValue2Value3(200),\n          [obj7, obj8, obj9],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1GreaterThanAnyValue2Value3(200, include: true),\n          [obj4, obj5, obj6, obj7, obj8, obj9],\n        );\n      });\n\n      isarTest('.lessThan()', () async {\n        await qEqual(\n          isar.models.where().value1LessThanAnyValue2Value3(null),\n          [],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1LessThanAnyValue2Value3(null, include: true),\n          [objNull1],\n        );\n        await qEqual(\n          isar.models.where().value1LessThanAnyValue2Value3(10),\n          [objNull1],\n        );\n        await qEqual(\n          isar.models.where().value1LessThanAnyValue2Value3(10, include: true),\n          [objNull1, objNull2, objNull3],\n        );\n      });\n\n      isarTest('.between()', () async {\n        await qEqual(\n          isar.models.where().value1BetweenAnyValue2Value3(null, 100),\n          [objNull1, objNull2, objNull3, obj1, obj2, obj3],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1BetweenAnyValue2Value3(null, 100, includeLower: false),\n          [objNull2, objNull3, obj1, obj2, obj3],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1BetweenAnyValue2Value3(null, 100, includeUpper: false),\n          [objNull1, objNull2, objNull3],\n        );\n        await qEqual(\n          isar.models.where().value1BetweenAnyValue2Value3(\n                null,\n                100,\n                includeLower: false,\n                includeUpper: false,\n              ),\n          [objNull2, objNull3],\n        );\n      });\n\n      isarTest('.isNull()', () async {\n        await qEqual(\n          isar.models.where().value1IsNullAnyValue2Value3(),\n          [objNull1],\n        );\n      });\n\n      isarTest('.isNotNull()', () async {\n        await qEqual(\n          isar.models.where().value1IsNotNullAnyValue2Value3(),\n          [\n            objNull2, objNull3, obj1, obj2, //\n            obj3, obj4, obj5, obj6, obj7, obj8, obj9 //\n          ],\n        );\n      });\n    });\n\n    group('value2', () {\n      isarTest('.equalTo()', () async {\n        await qEqual(\n          isar.models.where().value1Value2EqualToAnyValue3(null, null),\n          [],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToAnyValue3(100, 10),\n          [obj1, obj2],\n        );\n      });\n\n      isarTest('.notEqualTo()', () async {\n        await qEqual(\n          isar.models.where().value1EqualToValue2NotEqualToAnyValue3(200, 100),\n          [obj4, obj5, obj6],\n        );\n        await qEqual(\n          isar.models\n              .where(sort: Sort.desc)\n              .value1EqualToValue2NotEqualToAnyValue3(200, 100),\n          [obj6, obj5, obj4],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2NotEqualToAnyValue3(200, 20),\n          [obj4, obj5],\n        );\n      });\n\n      isarTest('.greaterThan()', () async {\n        await qEqual(\n          isar.models\n              .where()\n              .value1EqualToValue2GreaterThanAnyValue3(100, null),\n          [obj1, obj2, obj3],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2GreaterThanAnyValue3(100, 10),\n          [obj3],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1EqualToValue2GreaterThanAnyValue3(100, 10, include: true),\n          [obj1, obj2, obj3],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2GreaterThanAnyValue3(100, 20),\n          [],\n        );\n      });\n\n      isarTest('.lessThan()', () async {\n        await qEqual(\n          isar.models.where().value1EqualToValue2LessThanAnyValue3(300, 100),\n          [obj7, obj8, obj9],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2LessThanAnyValue3(300, 10),\n          [],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1EqualToValue2LessThanAnyValue3(300, 10, include: true),\n          [obj7, obj8],\n        );\n      });\n\n      isarTest('.between()', () async {\n        await qEqual(\n          isar.models\n              .where()\n              .value1EqualToValue2BetweenAnyValue3(200, null, 100),\n          [obj4, obj5, obj6],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2BetweenAnyValue3(200, 10, 20),\n          [obj4, obj5, obj6],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2BetweenAnyValue3(\n                200,\n                10,\n                20,\n                includeLower: false,\n              ),\n          [obj6],\n        );\n        await qEqual(\n          isar.models.where().value1EqualToValue2BetweenAnyValue3(\n                200,\n                10,\n                20,\n                includeUpper: false,\n              ),\n          [obj4, obj5],\n        );\n      });\n\n      isarTest('.isNull()', () async {\n        await qEqual(\n          isar.models.where().value1EqualToValue2IsNullAnyValue3(10),\n          [objNull2],\n        );\n      });\n\n      isarTest('.isNotNull()', () async {\n        await qEqual(\n          isar.models.where().value1EqualToValue2IsNotNullAnyValue3(10),\n          [objNull3],\n        );\n      });\n    });\n\n    group('value3', () {\n      isarTest('.equalTo()', () async {\n        await qEqual(\n          isar.models.where().value1Value2Value3EqualTo(null, null, null),\n          [],\n        );\n        await qEqual(\n          isar.models.where().value1Value2Value3EqualTo(100, 10, 1),\n          [obj1],\n        );\n      });\n\n      isarTest('.notEqualTo()', () async {\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3NotEqualTo(200, 10, 5),\n          [obj4, obj5],\n        );\n        await qEqual(\n          isar.models\n              .where(sort: Sort.desc)\n              .value1Value2EqualToValue3NotEqualTo(200, 10, 5),\n          [obj5, obj4],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3NotEqualTo(200, 10, 1),\n          [obj5],\n        );\n      });\n\n      isarTest('.greaterThan()', () async {\n        await qEqual(\n          isar.models\n              .where()\n              .value1Value2EqualToValue3GreaterThan(300, 10, null),\n          [obj7, obj8],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3GreaterThan(300, 10, 2),\n          [],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1Value2EqualToValue3GreaterThan(300, 10, 2, include: true),\n          [obj8],\n        );\n      });\n\n      isarTest('.lessThan()', () async {\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3LessThan(300, 10, 5),\n          [obj7, obj8],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3LessThan(300, 10, 1),\n          [],\n        );\n        await qEqual(\n          isar.models\n              .where()\n              .value1Value2EqualToValue3LessThan(300, 10, 1, include: true),\n          [obj7],\n        );\n      });\n\n      isarTest('.between()', () async {\n        await qEqual(\n          isar.models\n              .where()\n              .value1Value2EqualToValue3Between(200, 10, null, 5),\n          [obj4, obj5],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3Between(\n                200,\n                10,\n                1,\n                2,\n                includeLower: false,\n              ),\n          [obj5],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3Between(\n                200,\n                10,\n                1,\n                2,\n                includeUpper: false,\n              ),\n          [obj4],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3Between(\n                200,\n                10,\n                1,\n                2,\n                includeLower: false,\n                includeUpper: false,\n              ),\n          [],\n        );\n      });\n\n      isarTest('.isNull()', () async {\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3IsNull(10, 1),\n          [objNull3],\n        );\n      });\n\n      isarTest('.isNotNull()', () async {\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3IsNotNull(10, 1),\n          [],\n        );\n        await qEqual(\n          isar.models.where().value1Value2EqualToValue3IsNotNull(100, 10),\n          [obj1, obj2],\n        );\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/composite_string_test.dart",
    "content": "// ignore_for_file: require_trailing_commas\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'composite_string_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.value1, this.value2);\n\n  Id? id;\n\n  @Index(\n    composite: [\n      CompositeIndex(\n        'value2',\n        type: IndexType.value,\n      )\n    ],\n    unique: true,\n  )\n  String? value1;\n\n  String? value2;\n\n  @override\n  String toString() {\n    return '{id: $id, value1: $value1, value2: $value2}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return (other is Model) &&\n        other.id == id &&\n        other.value1 == value1 &&\n        other.value2 == value2;\n  }\n}\n\nvoid main() {\n  group('Composite String index', () {\n    late Isar isar;\n    late IsarCollection<Model> col;\n\n    late Model obj0;\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n    late Model obj4;\n    late Model obj5;\n    late Model obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n      col = isar.models;\n\n      obj0 = Model(null, null);\n      obj1 = Model('', 'a');\n      obj2 = Model('a', null);\n      obj3 = Model('a', '');\n      obj4 = Model('a', 'a');\n      obj5 = Model('a', 'b');\n      obj6 = Model('b', '');\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll([obj5, obj3, obj0, obj1, obj4, obj6, obj2]),\n      );\n    });\n\n    isarTest('getBy value1 sorted by value2', () async {\n      await qEqual(\n        col.where().value1EqualToAnyValue2('a'),\n        [obj2, obj3, obj4, obj5],\n      );\n      await qEqual(col.where().value1EqualToAnyValue2(null), [obj0]);\n    });\n\n    group('value1', () {\n      isarTest('.equalTo()', () async {\n        await qEqual(\n          col.where().value1EqualToAnyValue2('a'),\n          [obj2, obj3, obj4, obj5],\n        );\n        await qEqual(col.where().value1EqualToAnyValue2(null), [obj0]);\n        await qEqual(col.where().value1EqualToAnyValue2('c'), []);\n      });\n\n      isarTest('.notEqualTo()', () async {\n        await qEqualSet(\n          col.where().value1NotEqualToAnyValue2('a'),\n          [obj0, obj1, obj6],\n        );\n        await qEqualSet(\n          col.where().value1NotEqualToAnyValue2(''),\n          [obj0, obj2, obj3, obj4, obj5, obj6],\n        );\n        await qEqualSet(\n          col.where().value1NotEqualToAnyValue2('c'),\n          [obj0, obj1, obj2, obj3, obj4, obj5, obj6],\n        );\n      });\n\n      isarTest('.isNull()', () async {\n        await qEqual(col.where().value1IsNullAnyValue2(), [obj0]);\n      });\n\n      isarTest('.isNotNull()', () async {\n        await qEqualSet(\n          col.where().value1IsNotNullAnyValue2(),\n          [obj1, obj2, obj3, obj4, obj5, obj6],\n        );\n      });\n    });\n\n    group('value2', () {\n      isarTest('.equalTo()', () async {\n        await qEqual(col.where().value1Value2EqualTo(null, null), [obj0]);\n        await qEqual(col.where().value1Value2EqualTo('a', null), [obj2]);\n        await qEqual(col.where().value1Value2EqualTo('c', null), []);\n        await qEqual(col.where().value1Value2EqualTo('a', 'c'), []);\n      });\n\n      isarTest('.notEqualTo()', () async {\n        await qEqual(\n          col.where().value1EqualToValue2NotEqualTo('a', null),\n          [obj3, obj4, obj5],\n        );\n        await qEqual(\n          col.where().value1EqualToValue2NotEqualTo('a', 'c'),\n          [obj2, obj3, obj4, obj5],\n        );\n        await qEqual(col.where().value1EqualToValue2NotEqualTo('b', ''), []);\n      });\n\n      isarTest('.isNull()', () async {\n        await qEqual(col.where().value1EqualToValue2IsNull('a'), [obj2]);\n        await qEqual(col.where().value1EqualToValue2IsNull(null), [obj0]);\n        await qEqual(col.where().value1EqualToValue2IsNull(''), []);\n      });\n\n      isarTest('.isNotNull()', () async {\n        await qEqual(\n          col.where().value1EqualToValue2IsNotNull('a'),\n          [obj3, obj4, obj5],\n        );\n        await qEqual(col.where().value1EqualToValue2IsNotNull(null), []);\n      });\n\n      isarTest('.greaterThan()', () async {\n        await qEqual(\n          col.where().value1EqualToValue2GreaterThan('a', ''),\n          [obj4, obj5],\n        );\n        await qEqual(\n          col.where().value1EqualToValue2GreaterThan('a', '', include: true),\n          [obj3, obj4, obj5],\n        );\n        await qEqual(col.where().value1EqualToValue2GreaterThan('a', 'b'), []);\n      });\n\n      isarTest('.lessThan()', () async {\n        await qEqual(\n          col.where().value1EqualToValue2LessThan('a', 'b'),\n          [obj2, obj3, obj4],\n        );\n        await qEqual(\n          col.where().value1EqualToValue2LessThan('a', 'b', include: true),\n          [obj2, obj3, obj4, obj5],\n        );\n        await qEqual(col.where().value1EqualToValue2LessThan('a', null), []);\n      });\n\n      isarTest('.between()', () async {\n        await qEqual(\n          col.where().value1EqualToValue2Between('a', null, 'b'),\n          [obj2, obj3, obj4, obj5],\n        );\n        await qEqual(\n          col\n              .where()\n              .value1EqualToValue2Between('a', null, 'b', includeUpper: false),\n          [obj2, obj3, obj4],\n        );\n        await qEqual(\n          col\n              .where()\n              .value1EqualToValue2Between('a', null, 'b', includeLower: false),\n          [obj3, obj4, obj5],\n        );\n        await qEqual(\n          col.where().value1EqualToValue2Between('a', null, 'b',\n              includeLower: false, includeUpper: false),\n          [obj3, obj4],\n        );\n        await qEqual(\n          col.where().value1EqualToValue2Between('a', 'a', 'b',\n              includeLower: false, includeUpper: false),\n          [],\n        );\n      });\n\n      isarTest('.startsWith()', () async {\n        await qEqual(\n          col.where().value1EqualToValue2StartsWith('a', ''),\n          [obj3, obj4, obj5],\n        );\n        await qEqual(\n          col.where().value1EqualToValue2StartsWith('a', 'a'),\n          [obj4],\n        );\n        await qEqual(col.where().value1EqualToValue2StartsWith('a', 'c'), []);\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/get_by_delete_by_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'get_by_delete_by_test.g.dart';\n\n@collection\nclass Model {\n  Model({required this.id, required this.guid, required this.content});\n  final Id? id;\n\n  @Index(unique: true, type: IndexType.value)\n  final String guid;\n\n  @Index(unique: true, composite: [CompositeIndex('guid')])\n  final String content;\n\n  @override\n  String toString() {\n    return '{id: $id, guid: $guid, content: $content}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is Model &&\n        other.id == id &&\n        other.guid == guid &&\n        other.content == content;\n  }\n}\n\nvoid main() {\n  group('IndexGet', () {\n    late Isar isar;\n    late IsarCollection<Model> col;\n\n    late Model obj1;\n    late Model obj2;\n    late Model obj3;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n      col = isar.models;\n\n      obj1 = Model(id: 1, guid: 'AAA-001', content: 'A');\n      obj2 = Model(id: 2, guid: 'BBB-002', content: 'B');\n      obj3 = Model(id: 3, guid: 'CCC-003', content: 'C');\n\n      await isar.writeTxn(() async {\n        await col.putAll([obj1, obj2, obj3]);\n      });\n    });\n\n    isarTest('getBy', () async {\n      expect(await col.getByGuid(obj1.guid), obj1);\n      expect(await col.getByGuid(obj2.guid), obj2);\n      expect(await col.getByGuid(obj3.guid), obj3);\n      expect(await col.getByGuid('SOMETHING'), null);\n\n      expect(await col.getByContentGuid('A', obj1.guid), obj1);\n      expect(await col.getByContentGuid('B', obj1.guid), null);\n    });\n\n    isarTest('getAllBy', () async {\n      expect(\n        await col.getAllByGuid([obj3.guid, 'SOMETHING', obj1.guid]),\n        [obj3, null, obj1],\n      );\n\n      expect(\n        await col.getAllByContentGuid(\n          ['C', 'X', 'A'],\n          [obj3.guid, 'SOMETHING', obj1.guid],\n        ),\n        [obj3, null, obj1],\n      );\n    });\n\n    isarTestVm('getBySync', () {\n      expect(col.getByGuidSync(obj1.guid), obj1);\n      expect(col.getByGuidSync(obj2.guid), obj2);\n      expect(col.getByGuidSync(obj3.guid), obj3);\n      expect(col.getByGuidSync('SOMETHING'), null);\n\n      expect(col.getByContentGuidSync('A', obj1.guid), obj1);\n      expect(col.getByContentGuidSync('B', obj1.guid), null);\n    });\n\n    isarTestVm('getAllBySync', () {\n      expect(col.getAllByGuidSync([obj3.guid, obj1.guid]), [obj3, obj1]);\n\n      expect(\n        col.getAllByContentGuidSync(\n          ['C', 'X', 'A'],\n          [obj3.guid, 'SOMETHING', obj1.guid],\n        ),\n        [obj3, null, obj1],\n      );\n    });\n\n    isarTest('deleteBy', () async {\n      await isar.writeTxn(() async {\n        expect(await col.deleteByGuid(obj1.guid), true);\n        expect(await col.deleteByGuid('SOMETHING'), false);\n      });\n      await qEqual(col.where(), [obj2, obj3]);\n\n      await isar.writeTxn(() async {\n        expect(await col.deleteByContentGuid('B', obj2.guid), true);\n        expect(await col.deleteByContentGuid('D', obj3.guid), false);\n      });\n      await qEqual(col.where(), [obj3]);\n    });\n\n    isarTest('deleteAllBy', () async {\n      await isar.writeTxn(() async {\n        expect(await col.deleteAllByGuid([obj3.guid, obj1.guid, 'AAA']), 2);\n      });\n      await qEqual(col.where(), [obj2]);\n    });\n\n    isarTest('deleteAllBy composite', () async {\n      await isar.writeTxn(() async {\n        expect(\n          await col.deleteAllByContentGuid(\n            ['C', 'A', 'D'],\n            [obj3.guid, obj1.guid, obj2.guid],\n          ),\n          2,\n        );\n      });\n      await qEqual(col.where(), [obj2]);\n    });\n\n    isarTestVm('deleteBySync', () {\n      isar.writeTxnSync(() {\n        expect(col.deleteByGuidSync(obj1.guid), true);\n        expect(col.deleteByGuidSync('SOMETHING'), false);\n      });\n      expect(col.where().findAllSync(), [obj2, obj3]);\n\n      isar.writeTxnSync(() {\n        expect(col.deleteByContentGuidSync('B', obj2.guid), true);\n        expect(col.deleteByContentGuidSync('D', obj3.guid), false);\n      });\n      expect(col.where().findAllSync(), [obj3]);\n    });\n\n    isarTestVm('deleteAllBySync', () {\n      isar.writeTxnSync(() {\n        expect(col.deleteAllByGuidSync([obj3.guid, obj1.guid, 'AAA']), 2);\n      });\n      expect(col.where().findAllSync(), [obj2]);\n    });\n\n    isarTestVm('deleteAllBySync composite', () {\n      isar.writeTxnSync(() {\n        expect(\n          col.deleteAllByContentGuidSync(\n            ['C', 'A', 'D'],\n            [obj3.guid, obj1.guid, obj2.guid],\n          ),\n          2,\n        );\n      });\n      expect(col.where().findAllSync(), [obj2]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/multi_entry_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'multi_entry_test.g.dart';\n\n@collection\nclass MultiEntryIndexModel {\n  MultiEntryIndexModel({\n    required this.bools,\n    required this.ints,\n    required this.doubles,\n    required this.dateTimes,\n    required this.stringsSensitive,\n    required this.stringsInsensitive,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(type: IndexType.value)\n  final List<bool> bools;\n\n  @Index(type: IndexType.value)\n  final List<int> ints;\n\n  @Index(type: IndexType.value)\n  final List<double> doubles;\n\n  @Index(type: IndexType.value)\n  final List<DateTime> dateTimes;\n\n  @Index(type: IndexType.value, caseSensitive: true)\n  final List<String> stringsSensitive;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  final List<String> stringsInsensitive;\n\n  @override\n  String toString() {\n    return 'MultiEntryIndexModel{id: $id}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is MultiEntryIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(bools, other.bools) &&\n          listEquals(ints, other.ints) &&\n          listEquals(doubles, other.doubles) &&\n          listEquals(dateTimes, other.dateTimes) &&\n          listEquals(stringsSensitive, other.stringsSensitive) &&\n          listEquals(stringsInsensitive, other.stringsInsensitive);\n}\n\n@collection\nclass MultiEntryNullableIndexModel {\n  MultiEntryNullableIndexModel({\n    required this.bools,\n    required this.ints,\n    required this.doubles,\n    required this.dateTimes,\n    required this.stringsSensitive,\n    required this.stringsInsensitive,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(type: IndexType.value)\n  final List<bool?>? bools;\n\n  @Index(type: IndexType.value)\n  final List<int?>? ints;\n\n  @Index(type: IndexType.value)\n  final List<double?>? doubles;\n\n  @Index(type: IndexType.value)\n  final List<DateTime?>? dateTimes;\n\n  @Index(type: IndexType.value, caseSensitive: true)\n  final List<String?>? stringsSensitive;\n\n  @Index(type: IndexType.value, caseSensitive: false)\n  final List<String?>? stringsInsensitive;\n\n  @override\n  String toString() {\n    return 'MultiEntryIndexModel{id: $id}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is MultiEntryNullableIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(bools, other.bools) &&\n          listEquals(ints, other.ints) &&\n          listEquals(doubles, other.doubles) &&\n          listEquals(dateTimes, other.dateTimes) &&\n          listEquals(stringsSensitive, other.stringsSensitive) &&\n          listEquals(stringsInsensitive, other.stringsInsensitive);\n}\n\nvoid main() {\n  group('Multi entry index', () {\n    late Isar isar;\n\n    late MultiEntryIndexModel obj0;\n    late MultiEntryIndexModel obj1;\n    late MultiEntryIndexModel obj2;\n    late MultiEntryIndexModel obj3;\n    late MultiEntryIndexModel obj4;\n    late MultiEntryIndexModel obj5;\n\n    setUp(() async {\n      isar = await openTempIsar([MultiEntryIndexModelSchema]);\n\n      obj0 = MultiEntryIndexModel(\n        bools: [true, true],\n        ints: [0, 12, 0],\n        doubles: [0.0, 42.4, 52, 1],\n        dateTimes: [DateTime(2020)],\n        stringsSensitive: ['Tomatoes', 'Foo'],\n        stringsInsensitive: ['FOO', 'baR', 'FoO'],\n      );\n      obj1 = MultiEntryIndexModel(\n        bools: [],\n        ints: [28, 65535, 400],\n        doubles: [3.141592658979],\n        dateTimes: [DateTime(1920, 8, 9), DateTime(2000, 2, 2)],\n        stringsSensitive: ['books', 'foo'],\n        stringsInsensitive: ['BAR'],\n      );\n      obj2 = MultiEntryIndexModel(\n        bools: [],\n        ints: [],\n        doubles: [],\n        dateTimes: [],\n        stringsSensitive: [],\n        stringsInsensitive: [],\n      );\n      obj3 = MultiEntryIndexModel(\n        bools: [false],\n        ints: [123, -123, -0],\n        doubles: [123.512312512, 1123.43],\n        dateTimes: [DateTime(1921, 1, 29), DateTime(1456, 3, 11)],\n        stringsSensitive: ['fork', 'John'],\n        stringsInsensitive: ['PoTaToEs', ''],\n      );\n      obj4 = MultiEntryIndexModel(\n        bools: [true, true, true, false],\n        ints: [42, 0042, 0xffff],\n        doubles: [0x0042, 43.4123],\n        dateTimes: [DateTime(2022, 1, 3), DateTime(1921, 1, 29)],\n        stringsSensitive: ['forks'],\n        stringsInsensitive: ['potato', 'TOMATO'],\n      );\n      obj5 = MultiEntryIndexModel(\n        bools: [false, false, false],\n        ints: [-154123, 3],\n        doubles: [234.34, -123e3],\n        dateTimes: [DateTime(1204512, 1, 3), DateTime(1970)],\n        stringsSensitive: ['FORK', 'Joe'],\n        stringsInsensitive: ['foo', 'bar', 'tomato', 'fries'],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.multiEntryIndexModels.tPutAll([\n          obj0,\n          obj1,\n          obj2,\n          obj3,\n          obj4,\n          obj5,\n        ]),\n      );\n    });\n\n    isarTest('Bools query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().boolsElementEqualTo(true),\n        [obj0, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().boolsElementNotEqualTo(false),\n        [obj0, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().boolsElementEqualTo(false),\n        [obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().boolsElementNotEqualTo(true),\n        [obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().anyBoolsElement(),\n        [obj0, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .boolsElementEqualTo(true)\n            .or()\n            .boolsElementEqualTo(true)\n            .or()\n            .boolsElementEqualTo(true),\n        [obj0, obj4],\n      );\n    });\n\n    isarTest('Ints query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementEqualTo(42),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementEqualTo(0xffff),\n        [obj1, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementNotEqualTo(0xffff),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementLessThan(0),\n        [obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .intsElementLessThan(0, include: true),\n        [obj0, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementGreaterThan(42),\n        [obj1, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementNotEqualTo(0),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().intsElementBetween(0, 20),\n        [obj0, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .intsElementEqualTo(0xffff)\n            .or()\n            .intsElementLessThan(-42),\n        [obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .intsElementBetween(0, 8)\n            .or()\n            .intsElementBetween(0, 9),\n        [obj0, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().anyIntsElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Doubles query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().doublesElementLessThan(0),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().doublesElementGreaterThan(123.456),\n        [obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().doublesElementBetween(2, 64),\n        [obj0, obj1, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().anyDoublesElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .doublesElementGreaterThan(123.456)\n            .or()\n            .doublesElementGreaterThan(123.456),\n        [obj3, obj5],\n      );\n    });\n\n    isarTest('DateTimes query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementEqualTo(DateTime(1970)),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementEqualTo(DateTime(2000, 2, 2)),\n        [obj1],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementNotEqualTo(DateTime(2020)),\n        [obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementLessThan(DateTime(2000)),\n        [obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementLessThan(DateTime(0)),\n        [],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementGreaterThan(DateTime(2010, 4, 22)),\n        [obj0, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementBetween(DateTime(2005), DateTime(2030)),\n        [obj0, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().anyDateTimesElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .dateTimesElementEqualTo(DateTime(1970))\n            .or()\n            .dateTimesElementBetween(DateTime(1969), DateTime(1971)),\n        [obj5],\n      );\n    });\n\n    isarTest('Strings sensitive query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementEqualTo('fork'),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementNotEqualTo('forks'),\n        [obj0, obj1, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementLessThan('Greek'),\n        [obj0, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementGreaterThan('bee'),\n        [obj1, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementBetween('Fork', 'Potato'),\n        [obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementStartsWith('FO'),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementStartsWith('fork'),\n        [obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementStartsWith('Qwerty'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().anyStringsSensitiveElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsSensitiveElementEqualTo('foo')\n            .or()\n            .stringsSensitiveElementEqualTo('foo'),\n        [obj1],\n      );\n    });\n\n    isarTest('Strings insensitive query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsInsensitiveElementEqualTo('foo'),\n        [obj0, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().stringsInsensitiveElementEqualTo(''),\n        [obj3],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsInsensitiveElementNotEqualTo('bar'),\n        [obj0, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsInsensitiveElementLessThan('D'),\n        [obj0, obj1, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsInsensitiveElementGreaterThan('KeTcHuP'),\n        [obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsInsensitiveElementBetween('abc', 'def'),\n        [obj0, obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels.where().anyStringsInsensitiveElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Multi index query', () async {\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .boolsElementEqualTo(true)\n            .or()\n            .dateTimesElementEqualTo(DateTime(2020))\n            .or()\n            .intsElementEqualTo(123),\n        [obj0, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryIndexModels\n            .where()\n            .stringsInsensitiveElementStartsWith('po')\n            .or()\n            .stringsSensitiveElementStartsWith('for')\n            .or()\n            .boolsElementNotEqualTo(false),\n        [obj0, obj3, obj4],\n      );\n    });\n  });\n\n  group('Multi entry nullable index', () {\n    late Isar isar;\n\n    late MultiEntryNullableIndexModel obj0;\n    late MultiEntryNullableIndexModel obj1;\n    late MultiEntryNullableIndexModel obj2;\n    late MultiEntryNullableIndexModel obj3;\n    late MultiEntryNullableIndexModel obj4;\n    late MultiEntryNullableIndexModel obj5;\n\n    setUp(() async {\n      isar = await openTempIsar([MultiEntryNullableIndexModelSchema]);\n\n      obj0 = MultiEntryNullableIndexModel(\n        bools: [true, null],\n        ints: null,\n        doubles: [42.42, 20, -400],\n        dateTimes: [DateTime(2020), DateTime(2030, 3, 23)],\n        stringsSensitive: [null, ''],\n        stringsInsensitive: ['FOO', 'baR'],\n      );\n      obj1 = MultiEntryNullableIndexModel(\n        bools: [null],\n        ints: [42, 64, 32],\n        doubles: null,\n        dateTimes: null,\n        stringsSensitive: ['Tomatoes', 'Potatoes'],\n        stringsInsensitive: ['foo', 'BAR', null],\n      );\n      obj2 = MultiEntryNullableIndexModel(\n        bools: null,\n        ints: null,\n        doubles: null,\n        dateTimes: null,\n        stringsSensitive: null,\n        stringsInsensitive: null,\n      );\n      obj3 = MultiEntryNullableIndexModel(\n        bools: [null],\n        ints: [null],\n        doubles: [null],\n        dateTimes: [null],\n        stringsSensitive: [null],\n        stringsInsensitive: [null],\n      );\n      obj4 = MultiEntryNullableIndexModel(\n        bools: [true, true, false],\n        ints: [null, null, 29],\n        doubles: [3.14159265358979, null, 0],\n        dateTimes: [\n          DateTime(1970, 2, 8),\n          DateTime(2000, 4, 3),\n          DateTime(1560, 8, 27),\n        ],\n        stringsSensitive: ['potato', 'fries', 'rice'],\n        stringsInsensitive: ['BAr', null, ''],\n      );\n      obj5 = MultiEntryNullableIndexModel(\n        bools: [true, null, false, null, true],\n        ints: [99, 0xffff, 42, 32],\n        doubles: [null, 24.32, 41.43],\n        dateTimes: [null, DateTime(1999, 3, 21)],\n        stringsSensitive: ['', '', 'Potatoes'],\n        stringsInsensitive: ['bar', 'BAR', null, 'foo'],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.multiEntryNullableIndexModels.tPutAll([\n          obj0,\n          obj1,\n          obj2,\n          obj3,\n          obj4,\n          obj5,\n        ]),\n      );\n    });\n\n    isarTest('Bools query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().boolsElementEqualTo(true),\n        [obj0, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .boolsElementNotEqualTo(false),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().boolsElementIsNull(),\n        [obj0, obj1, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().boolsElementIsNotNull(),\n        [obj0, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().anyBoolsElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Ints query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementEqualTo(42),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementNotEqualTo(42),\n        [obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementLessThan(0),\n        [obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementGreaterThan(90),\n        [obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementBetween(0, 50),\n        [obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementIsNotNull(),\n        [obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().intsElementIsNull(),\n        [obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().anyIntsElement(),\n        [obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Doubles query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().doublesElementLessThan(10),\n        [obj0, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .doublesElementGreaterThan(40),\n        [obj0, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().doublesElementBetween(0, 32),\n        [obj0, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().doublesElementIsNull(),\n        [obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().doublesElementIsNotNull(),\n        [obj0, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().anyDoublesElement(),\n        [obj0, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('DateTimes query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .dateTimesElementEqualTo(DateTime(1999, 3, 21))\n            .or()\n            .dateTimesElementEqualTo(DateTime(2020)),\n        [obj0, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .dateTimesElementNotEqualTo(DateTime(2020)),\n        [obj0, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .dateTimesElementLessThan(DateTime(2000)),\n        [obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .dateTimesElementGreaterThan(DateTime(2005, 1, 4)),\n        [obj0],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .dateTimesElementBetween(DateTime(2000), DateTime(2020)),\n        [obj0, obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().dateTimesElementIsNull(),\n        [obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().dateTimesElementIsNotNull(),\n        [obj0, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().anyDateTimesElement(),\n        [obj0, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Strings sensitive query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementEqualTo('Potatoes'),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementEqualTo(''),\n        [obj0, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementLessThan('aaaa'),\n        [obj0, obj1, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementGreaterThan('aaaa'),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementBetween('P', 'b'),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementIsNull(),\n        [obj0, obj3],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementIsNotNull(),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementStartsWith('Po'),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementStartsWith(''),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels.where().anyStringsSensitiveElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Strings insensitive query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementEqualTo('bAR'),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementEqualTo(''),\n        [obj4],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementLessThan('desert'),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementGreaterThan('bar'),\n        [obj0, obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementBetween('a', 'C'),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementIsNull(),\n        [obj1, obj3, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementIsNotNull(),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementStartsWith('ba'),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsInsensitiveElementStartsWith('f'),\n        [obj0, obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .anyStringsInsensitiveElement(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n    });\n\n    isarTest('Multi index query', () async {\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .boolsElementIsNull()\n            .or()\n            .dateTimesElementIsNull(),\n        [obj0, obj1, obj3, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .intsElementIsNotNull()\n            .or()\n            .dateTimesElementEqualTo(DateTime(2020)),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .stringsSensitiveElementStartsWith('fo')\n            .or()\n            .stringsInsensitiveElementStartsWith('b'),\n        [obj0, obj1, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.multiEntryNullableIndexModels\n            .where()\n            .dateTimesElementGreaterThan(DateTime(2020))\n            .or()\n            .stringsInsensitiveElementIsNull()\n            .or()\n            .stringsInsensitiveElementIsNull()\n            .or()\n            .stringsInsensitiveElementIsNull(),\n        [obj0, obj1, obj3, obj4, obj5],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/put_by_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'put_by_test.g.dart';\n\n@collection\nclass BoolIndexModel {\n  BoolIndexModel({\n    required this.value,\n    required this.index,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(unique: true)\n  final bool value;\n\n  final int index;\n\n  @override\n  String toString() {\n    return 'BoolIndexModel{id: $id, value: $value, index: $index}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is BoolIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          value == other.value &&\n          index == other.index;\n}\n\n@collection\nclass IntIndexModel {\n  IntIndexModel({\n    required this.value,\n    required this.index,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(unique: true)\n  final int value;\n\n  final int index;\n\n  @override\n  String toString() {\n    return 'IntIndexModel{id: $id, value: $value, index: $index}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is IntIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          value == other.value &&\n          index == other.index;\n}\n\n@collection\nclass DoubleIndexModel {\n  DoubleIndexModel({\n    required this.value,\n    required this.index,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(unique: true)\n  final double value;\n\n  final int index;\n\n  @override\n  String toString() {\n    return 'DoubleIndexModel{id: $id, value: $value, index: $index}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is DoubleIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          value == other.value &&\n          index == other.index;\n}\n\n@collection\nclass StringValueIndexModel {\n  StringValueIndexModel({\n    required this.value,\n    required this.index,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(unique: true, type: IndexType.value)\n  final String value;\n\n  final int index;\n\n  @override\n  String toString() {\n    return 'StringValueIndexModel{id: $id, value: $value, index: $index}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is StringValueIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          value == other.value &&\n          index == other.index;\n}\n\n@collection\nclass StringHashIndexModel {\n  StringHashIndexModel({\n    required this.value,\n    required this.index,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(unique: true, type: IndexType.hash)\n  final String value;\n\n  final int index;\n\n  @override\n  String toString() {\n    return 'StringHashIndexModel{id: $id, value: $value, index: $index}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is StringHashIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          value == other.value &&\n          index == other.index;\n}\n\n@collection\nclass StringInsensitiveIndexModel {\n  StringInsensitiveIndexModel({\n    required this.value,\n    required this.index,\n  });\n\n  Id id = Isar.autoIncrement;\n\n  @Index(unique: true, caseSensitive: false)\n  final String value;\n\n  final int index;\n\n  @override\n  String toString() {\n    return 'StringInsensitiveIndexModel{id: $id, value: $value, index: $index}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is StringInsensitiveIndexModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          value == other.value &&\n          index == other.index;\n}\n\nvoid main() {\n  group('Put by index', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([\n        BoolIndexModelSchema,\n        IntIndexModelSchema,\n        DoubleIndexModelSchema,\n        StringValueIndexModelSchema,\n        StringHashIndexModelSchema,\n        StringInsensitiveIndexModelSchema,\n      ]);\n    });\n\n    isarTest('Put by bool index', () async {\n      final obj0 = BoolIndexModel(value: true, index: 0);\n      final obj1 = BoolIndexModel(value: false, index: 1);\n      final obj2 = BoolIndexModel(value: true, index: 2);\n\n      await isar.tWriteTxn(() async {\n        await isar.boolIndexModels.tPutAllByValue([obj0, obj1]);\n        await isar.boolIndexModels.tPutByValue(obj2);\n      });\n\n      await qEqual(isar.boolIndexModels.where(), [obj2, obj1]);\n      expect(obj0.id, obj2.id);\n\n      await isar.tWriteTxn(() => isar.boolIndexModels.tPutByValue(obj0));\n      await qEqual(isar.boolIndexModels.where(), [obj0, obj1]);\n      expect(obj0.id, obj2.id);\n    });\n\n    isarTest('Put by int index', () async {\n      final obj0 = IntIndexModel(value: 42, index: 0);\n      final obj1 = IntIndexModel(value: 20, index: 1);\n      final obj2 = IntIndexModel(value: 42, index: 2);\n      final obj3 = IntIndexModel(value: 3, index: 3);\n\n      await isar.tWriteTxn(\n        () => isar.intIndexModels.tPutAllByValue([obj0, obj1, obj3]),\n      );\n      await qEqual(isar.intIndexModels.where(), [obj0, obj1, obj3]);\n\n      await isar.tWriteTxn(() => isar.intIndexModels.tPutByValue(obj2));\n      await qEqual(isar.intIndexModels.where(), [obj2, obj1, obj3]);\n      expect(obj0.id, obj2.id);\n    });\n\n    isarTest('Put by double index', () async {\n      final obj0 = DoubleIndexModel(value: 15.23, index: 0);\n      final obj1 = DoubleIndexModel(value: 15.23, index: 1);\n      final obj2 = DoubleIndexModel(value: 15.23, index: 2);\n      final obj3 = DoubleIndexModel(value: 0, index: 3);\n\n      await isar.tWriteTxn(\n        () => isar.doubleIndexModels.tPutAllByValue([obj0, obj1, obj2, obj3]),\n      );\n\n      await qEqual(isar.doubleIndexModels.where(), [obj2, obj3]);\n      expect(obj0.id, obj1.id);\n      expect(obj0.id, obj2.id);\n    });\n\n    isarTest('Put by string value index', () async {\n      final obj0 = StringValueIndexModel(value: 'Foo bar', index: 0);\n      final obj1 = StringValueIndexModel(value: 'foo Bar', index: 1);\n      final obj2 = StringValueIndexModel(value: 'Foo bar', index: 2);\n      final obj3 = StringValueIndexModel(value: 'John Doe', index: 3);\n\n      await isar.tWriteTxn(\n        () => isar.stringValueIndexModels.tPutAllByValue([obj0, obj1]),\n      );\n      await qEqual(isar.stringValueIndexModels.where(), [obj0, obj1]);\n\n      await isar.tWriteTxn(\n        () => isar.stringValueIndexModels.tPutAllByValue([obj2, obj3]),\n      );\n      await qEqual(isar.stringValueIndexModels.where(), [obj2, obj1, obj3]);\n      expect(obj0.id, obj2.id);\n\n      await isar.tWriteTxn(() => isar.stringValueIndexModels.tPutByValue(obj0));\n      await qEqual(isar.stringValueIndexModels.where(), [obj0, obj1, obj3]);\n      expect(obj0.id, obj2.id);\n    });\n\n    isarTest('Put by string hash index', () async {\n      final obj0 = StringHashIndexModel(value: 'abc', index: 0);\n      final obj1 = StringHashIndexModel(value: 'xyz', index: 1);\n      final obj2 = StringHashIndexModel(value: '123', index: 2);\n      final obj3 = StringHashIndexModel(value: 'xyz', index: 3);\n      final obj4 = StringHashIndexModel(value: 'abc', index: 4);\n\n      await isar.tWriteTxn(\n        () => isar.stringHashIndexModels.tPutAllByValue([obj0, obj1, obj2]),\n      );\n      await qEqual(isar.stringHashIndexModels.where(), [obj0, obj1, obj2]);\n\n      await isar.tWriteTxn(\n        () => isar.stringHashIndexModels.tPutAllByValue([obj3, obj4]),\n      );\n      await qEqual(isar.stringHashIndexModels.where(), [obj4, obj3, obj2]);\n      expect(obj0.id, obj4.id);\n      expect(obj1.id, obj3.id);\n\n      await isar.tWriteTxn(\n        () => isar.stringHashIndexModels.tPutByValue(obj0),\n      );\n      await qEqual(isar.stringHashIndexModels.where(), [obj0, obj3, obj2]);\n      expect(obj0.id, obj4.id);\n    });\n\n    isarTest('Put by string insensitive index', () async {\n      final obj0 = StringInsensitiveIndexModel(value: 'foo', index: 0);\n      final obj1 = StringInsensitiveIndexModel(value: 'BAR', index: 1);\n      final obj2 = StringInsensitiveIndexModel(value: 'Foo', index: 2);\n      final obj3 = StringInsensitiveIndexModel(value: 'abc', index: 3);\n      final obj4 = StringInsensitiveIndexModel(value: 'FoO', index: 4);\n      final obj5 = StringInsensitiveIndexModel(value: 'Bar', index: 5);\n\n      await isar.tWriteTxn(\n        () => isar.stringInsensitiveIndexModels\n            .tPutAllByValue([obj0, obj1, obj3]),\n      );\n      await qEqual(\n        isar.stringInsensitiveIndexModels.where(),\n        [obj0, obj1, obj3],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.stringInsensitiveIndexModels\n            .tPutAllByValue([obj0, obj3, obj5]),\n      );\n      await qEqual(\n        isar.stringInsensitiveIndexModels.where(),\n        [obj0, obj5, obj3],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.stringInsensitiveIndexModels\n            .tPutAllByValue([obj2, obj4, obj5]),\n      );\n      await qEqual(\n        isar.stringInsensitiveIndexModels.where(),\n        [obj4, obj5, obj3],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.stringInsensitiveIndexModels.tPutByValue(obj1),\n      );\n      await qEqual(\n        isar.stringInsensitiveIndexModels.where(),\n        [obj4, obj1, obj3],\n      );\n    });\n\n    isarTest('Put all by without items', () async {\n      final obj0 = BoolIndexModel(value: true, index: 0);\n      final obj1 = BoolIndexModel(value: true, index: 1);\n\n      await isar.tWriteTxn(() => isar.boolIndexModels.tPutAllByValue([]));\n      await qEqual(isar.boolIndexModels.where(), []);\n\n      await isar.tWriteTxn(() => isar.boolIndexModels.tPut(obj0));\n      await qEqual(isar.boolIndexModels.where(), [obj0]);\n\n      await isar.tWriteTxn(() => isar.boolIndexModels.tPutAllByValue([]));\n      await isar.tWriteTxn(() => isar.boolIndexModels.tPutAllByValue([obj1]));\n      await isar.tWriteTxn(() => isar.boolIndexModels.tPutAllByValue([]));\n\n      await qEqual(isar.boolIndexModels.where(), [obj1]);\n    });\n  });\n}\n\n// Extension methods for collections, in order to use\n// `tPutByValue` / `tPutAllByValue` on them\nextension TBoolIndexModelCollectionExt on IsarCollection<BoolIndexModel> {\n  Future<int> tPutByValue(BoolIndexModel obj) {\n    if (syncTest) {\n      return SynchronousFuture(putByValueSync(obj));\n    }\n    return putByValue(obj);\n  }\n\n  Future<List<int>> tPutAllByValue(List<BoolIndexModel> objs) {\n    if (syncTest) {\n      return SynchronousFuture(putAllByValueSync(objs));\n    }\n    return putAllByValue(objs);\n  }\n}\n\nextension TIntIndexModelCollectionExt on IsarCollection<IntIndexModel> {\n  Future<int> tPutByValue(IntIndexModel obj) {\n    if (syncTest) {\n      return SynchronousFuture(putByValueSync(obj));\n    }\n    return putByValue(obj);\n  }\n\n  Future<List<int>> tPutAllByValue(List<IntIndexModel> objs) {\n    if (syncTest) {\n      return SynchronousFuture(putAllByValueSync(objs));\n    }\n    return putAllByValue(objs);\n  }\n}\n\nextension TDoubleIndexModelCollectionExt on IsarCollection<DoubleIndexModel> {\n  Future<int> tPutByValue(DoubleIndexModel obj) {\n    if (syncTest) {\n      return SynchronousFuture(putByValueSync(obj));\n    }\n    return putByValue(obj);\n  }\n\n  Future<List<int>> tPutAllByValue(List<DoubleIndexModel> objs) {\n    if (syncTest) {\n      return SynchronousFuture(putAllByValueSync(objs));\n    }\n    return putAllByValue(objs);\n  }\n}\n\nextension TStringValueIndexModelCollectionExt\n    on IsarCollection<StringValueIndexModel> {\n  Future<int> tPutByValue(StringValueIndexModel obj) {\n    if (syncTest) {\n      return SynchronousFuture(putByValueSync(obj));\n    }\n    return putByValue(obj);\n  }\n\n  Future<List<int>> tPutAllByValue(List<StringValueIndexModel> objs) {\n    if (syncTest) {\n      return SynchronousFuture(putAllByValueSync(objs));\n    }\n    return putAllByValue(objs);\n  }\n}\n\nextension TStringHashIndexModelCollectionExt\n    on IsarCollection<StringHashIndexModel> {\n  Future<int> tPutByValue(StringHashIndexModel obj) {\n    if (syncTest) {\n      return SynchronousFuture(putByValueSync(obj));\n    }\n    return putByValue(obj);\n  }\n\n  Future<List<int>> tPutAllByValue(List<StringHashIndexModel> objs) {\n    if (syncTest) {\n      return SynchronousFuture(putAllByValueSync(objs));\n    }\n    return putAllByValue(objs);\n  }\n}\n\nextension TStringInsensitiveIndexModelCollectionExt\n    on IsarCollection<StringInsensitiveIndexModel> {\n  Future<int> tPutByValue(StringInsensitiveIndexModel obj) {\n    if (syncTest) {\n      return SynchronousFuture(putByValueSync(obj));\n    }\n    return putByValue(obj);\n  }\n\n  Future<List<int>> tPutAllByValue(List<StringInsensitiveIndexModel> objs) {\n    if (syncTest) {\n      return SynchronousFuture(putAllByValueSync(objs));\n    }\n    return putAllByValue(objs);\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_bool_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_bool_list_test.g.dart';\n\n@collection\nclass BoolModel {\n  BoolModel(this.list) : hashList = list;\n\n  Id? id;\n\n  @Index(type: IndexType.value)\n  List<bool?>? list;\n\n  @Index(type: IndexType.hash)\n  List<bool?>? hashList;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is BoolModel &&\n        other.id == id &&\n        listEquals(list, other.list) &&\n        listEquals(hashList, other.hashList);\n  }\n}\n\nvoid main() {\n  group('Where bool list', () {\n    late Isar isar;\n    late IsarCollection<BoolModel> col;\n\n    late BoolModel objEmpty;\n    late BoolModel obj1;\n    late BoolModel obj2;\n    late BoolModel obj3;\n    late BoolModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([BoolModelSchema]);\n      col = isar.boolModels;\n\n      objEmpty = BoolModel([]);\n      obj1 = BoolModel([true]);\n      obj2 = BoolModel([null, false]);\n      obj3 = BoolModel([true, false, true]);\n      objNull = BoolModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objEmpty, obj1, obj2, obj3, objNull]);\n      });\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqualSet(\n        col.where().listElementEqualTo(true),\n        [obj1, obj3],\n      );\n      await qEqualSet(col.where().listElementEqualTo(null), [obj2]);\n    });\n\n    isarTest('.elementNotEqualTo()', () async {\n      await qEqualSet(\n        col.where().listElementNotEqualTo(true),\n        [obj2, obj3],\n      );\n      await qEqualSet(\n        col.where().listElementNotEqualTo(null),\n        [obj1, obj2, obj3],\n      );\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqualSet(col.where().listElementIsNull(), [obj2]);\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqualSet(\n        col.where().listElementIsNotNull(),\n        [obj1, obj2, obj3],\n      );\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqualSet(col.where().hashListEqualTo(null), [objNull]);\n      await qEqualSet(col.where().hashListEqualTo([]), [objEmpty]);\n      await qEqualSet(\n        col.where().hashListEqualTo([null, false]),\n        [obj2],\n      );\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqualSet(\n        col.where().hashListNotEqualTo([]),\n        [objNull, obj1, obj2, obj3],\n      );\n      await qEqualSet(\n        col.where().hashListNotEqualTo([true, false, true]),\n        [objEmpty, obj1, obj2, objNull],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqualSet(col.where().hashListIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqualSet(\n        col.where().hashListIsNotNull(),\n        [objEmpty, obj1, obj2, obj3],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_bool_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_bool_test.g.dart';\n\n@collection\nclass BoolModel {\n  BoolModel(this.field);\n\n  Id? id;\n\n  @Index()\n  bool? field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is BoolModel && other.id == id && other.field == field;\n  }\n}\n\nvoid main() {\n  group('Where bool', () {\n    late Isar isar;\n    late IsarCollection<BoolModel> col;\n\n    late BoolModel objNull;\n    late BoolModel objFalse;\n    late BoolModel objTrue;\n    late BoolModel objFalse2;\n\n    setUp(() async {\n      isar = await openTempIsar([BoolModelSchema]);\n      col = isar.boolModels;\n\n      objNull = BoolModel(null);\n      objFalse = BoolModel(false);\n      objTrue = BoolModel(true);\n      objFalse2 = BoolModel(false);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objNull, objFalse, objTrue, objFalse2]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.where().fieldEqualTo(true), [objTrue]);\n      await qEqual(\n        col.where().fieldEqualTo(false),\n        [objFalse, objFalse2],\n      );\n      await qEqual(col.where().fieldEqualTo(null), [objNull]);\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqual(\n        col.where().fieldNotEqualTo(true),\n        [objNull, objFalse, objFalse2],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(false),\n        [objNull, objTrue],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(null),\n        [objFalse, objFalse2, objTrue],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.where().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(\n        col.where().fieldIsNotNull(),\n        [objFalse, objFalse2, objTrue],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_byte_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_byte_list_test.g.dart';\n\n@collection\nclass ByteModel {\n  ByteModel(this.list) : hashList = list;\n\n  Id? id;\n\n  @Index(type: IndexType.value)\n  List<byte>? list;\n\n  @Index(type: IndexType.hash)\n  List<byte>? hashList;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is ByteModel &&\n        other.id == id &&\n        listEquals(list, other.list) &&\n        listEquals(hashList, other.hashList);\n  }\n}\n\nvoid main() {\n  group('Where byte list', () {\n    late Isar isar;\n    late IsarCollection<ByteModel> col;\n\n    late ByteModel objEmpty;\n    late ByteModel obj1;\n    late ByteModel obj2;\n    late ByteModel obj3;\n    late ByteModel obj4;\n    late ByteModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([ByteModelSchema]);\n      col = isar.byteModels;\n\n      objEmpty = ByteModel([]);\n      obj1 = ByteModel([123]);\n      obj2 = ByteModel([0, 255]);\n      obj3 = ByteModel([1, 123, 3]);\n      obj4 = ByteModel([0, 255]);\n      objNull = ByteModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objEmpty, obj1, obj2, obj3, obj4, objNull]);\n      });\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqualSet(\n        col.where().listElementEqualTo(0),\n        [obj2, obj4],\n      );\n      await qEqualSet(col.where().listElementEqualTo(1), [obj3]);\n      await qEqualSet(\n        col.where().listElementEqualTo(55),\n        [],\n      );\n    });\n\n    isarTest('.elementNotEqualTo()', () async {\n      await qEqualSet(\n        col.where().listElementNotEqualTo(123),\n        [obj2, obj3, obj4],\n      );\n      await qEqualSet(\n        col.where().listElementNotEqualTo(0),\n        [obj1, obj2, obj3, obj4],\n      );\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqualSet(\n        col.where().listElementGreaterThan(123),\n        [obj2, obj4],\n      );\n      await qEqualSet(\n        col.where().listElementGreaterThan(123, include: true),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqualSet(\n        col.where().listElementGreaterThan(255),\n        [],\n      );\n    });\n\n    isarTest('.elementLessThan()', () async {\n      await qEqualSet(\n        col.where().listElementLessThan(123),\n        [obj2, obj3, obj4],\n      );\n      await qEqualSet(\n        col.where().listElementLessThan(123, include: true),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqualSet(col.where().listElementLessThan(0), []);\n    });\n\n    isarTest('.elementBetween()', () async {\n      await qEqualSet(\n        col.where().listElementBetween(123, 255),\n        [obj1, obj2, obj3, obj4],\n      );\n      await qEqualSet(\n        col.where().listElementBetween(123, 255, includeLower: false),\n        [obj2, obj4],\n      );\n      await qEqualSet(\n        col.where().listElementBetween(123, 255, includeUpper: false),\n        [obj1, obj3],\n      );\n      await qEqualSet(col.where().listElementBetween(50, 100), []);\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqualSet(col.where().hashListEqualTo(null), [objNull]);\n      await qEqualSet(col.where().hashListEqualTo([]), [objEmpty]);\n      await qEqualSet(\n        col.where().hashListEqualTo([0, 255]),\n        [obj2, obj4],\n      );\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqualSet(\n        col.where().hashListNotEqualTo([]),\n        [objNull, obj1, obj2, obj3, obj4],\n      );\n      await qEqualSet(\n        col.where().hashListNotEqualTo([0, 255]),\n        [objEmpty, obj1, obj3, objNull],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqualSet(col.where().hashListIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqualSet(\n        col.where().hashListIsNotNull(),\n        [objEmpty, obj1, obj2, obj3, obj4],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_byte_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_byte_test.g.dart';\n\n@collection\nclass ByteModel {\n  ByteModel(this.field);\n\n  Id? id;\n\n  @Index()\n  byte field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is ByteModel && other.id == id && other.field == field;\n  }\n}\n\nvoid main() {\n  group('Where byte', () {\n    late Isar isar;\n    late IsarCollection<ByteModel> col;\n\n    late ByteModel objMin;\n    late ByteModel obj1;\n    late ByteModel obj2;\n    late ByteModel obj3;\n    late ByteModel objMax;\n\n    setUp(() async {\n      isar = await openTempIsar([ByteModelSchema]);\n      col = isar.byteModels;\n\n      objMin = ByteModel(0);\n      obj1 = ByteModel(1);\n      obj2 = ByteModel(123);\n      obj3 = ByteModel(1);\n      objMax = ByteModel(255);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objMin, obj1, obj2, obj3, objMax]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.where().fieldEqualTo(0), [objMin]);\n      await qEqual(col.where().fieldEqualTo(1), [obj1, obj3]);\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqual(\n        col.where().fieldNotEqualTo(0),\n        [obj1, obj3, obj2, objMax],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(1),\n        [objMin, obj2, objMax],\n      );\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(\n        col.where().fieldGreaterThan(0),\n        [obj1, obj3, obj2, objMax],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(0, include: true),\n        [objMin, obj1, obj3, obj2, objMax],\n      );\n      await qEqual(col.where().fieldGreaterThan(255), []);\n      await qEqual(\n        col.where().fieldGreaterThan(255, include: true),\n        [objMax],\n      );\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(\n        col.where().fieldLessThan(255),\n        [objMin, obj1, obj3, obj2],\n      );\n      await qEqual(\n        col.where().fieldLessThan(255, include: true),\n        [objMin, obj1, obj3, obj2, objMax],\n      );\n      await qEqual(col.where().fieldLessThan(0), []);\n      await qEqual(\n        col.where().fieldLessThan(0, include: true),\n        [objMin],\n      );\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.where().fieldBetween(0, 255),\n        [objMin, obj1, obj3, obj2, objMax],\n      );\n      await qEqual(\n        col.where().fieldBetween(0, 255, includeLower: false),\n        [obj1, obj3, obj2, objMax],\n      );\n      await qEqual(\n        col.where().fieldBetween(0, 255, includeUpper: false),\n        [objMin, obj1, obj3, obj2],\n      );\n      await qEqual(col.where().fieldBetween(255, 0), []);\n      await qEqual(col.where().fieldBetween(100, 110), []);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_date_time_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_date_time_list_test.g.dart';\n\n@collection\nclass DateTimeModel {\n  DateTimeModel({\n    required this.values,\n    required this.nullableValues,\n    required this.valuesNullable,\n    required this.nullableValuesNullable,\n  })  : hash = values,\n        nullableHash = nullableValues,\n        hashNullable = valuesNullable,\n        nullableHashNullable = nullableValuesNullable;\n\n  Id id = Isar.autoIncrement;\n\n  @Index(type: IndexType.value)\n  List<DateTime> values;\n\n  @Index(type: IndexType.value)\n  List<DateTime?> nullableValues;\n\n  @Index(type: IndexType.value)\n  List<DateTime>? valuesNullable;\n\n  @Index(type: IndexType.value)\n  List<DateTime?>? nullableValuesNullable;\n\n  @Index(type: IndexType.hash)\n  List<DateTime> hash;\n\n  @Index(type: IndexType.hash)\n  List<DateTime?> nullableHash;\n\n  @Index(type: IndexType.hash)\n  List<DateTime>? hashNullable;\n\n  @Index(type: IndexType.hash)\n  List<DateTime?>? nullableHashNullable;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is DateTimeModel &&\n      id == other.id &&\n      dateTimeListEquals(values, other.values) &&\n      dateTimeListEquals(nullableValues, other.nullableValues) &&\n      dateTimeListEquals(valuesNullable, other.valuesNullable) &&\n      dateTimeListEquals(\n        nullableValuesNullable,\n        other.nullableValuesNullable,\n      ) &&\n      dateTimeListEquals(hash, other.hash) &&\n      dateTimeListEquals(nullableHash, other.nullableHash) &&\n      dateTimeListEquals(hashNullable, other.hashNullable) &&\n      dateTimeListEquals(nullableHashNullable, other.nullableHashNullable);\n\n  @override\n  String toString() {\n    return '''DateTimeModel{id: $id, values: $values, nullableValues: $nullableValues, valuesNullable: $valuesNullable, nullableValuesNullable: $nullableValuesNullable, hash: $hash, nullableHash: $nullableHash, hashNullable: $hashNullable, nullableHashNullable: $nullableHashNullable}''';\n  }\n}\n\nvoid main() {\n  group('Where DateTime list', () {\n    late Isar isar;\n\n    late DateTimeModel obj1;\n    late DateTimeModel obj2;\n    late DateTimeModel obj3;\n    late DateTimeModel obj4;\n    late DateTimeModel obj5;\n    late DateTimeModel obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([DateTimeModelSchema]);\n\n      obj1 = DateTimeModel(\n        values: [DateTime(2001), DateTime(2002), DateTime(2003).toUtc()],\n        nullableValues: [DateTime(2001), null, DateTime(2003).toUtc()],\n        valuesNullable: [DateTime(2001)],\n        nullableValuesNullable: [DateTime(2001), null, null],\n      );\n      obj2 = DateTimeModel(\n        values: [DateTime(2002), DateTime(2004).toUtc()],\n        nullableValues: [\n          DateTime(2002),\n          DateTime(2003),\n          DateTime(2003).toUtc(),\n        ],\n        valuesNullable: null,\n        nullableValuesNullable: null,\n      );\n      obj3 = DateTimeModel(\n        values: [],\n        nullableValues: [],\n        valuesNullable: [],\n        nullableValuesNullable: [],\n      );\n      obj4 = DateTimeModel(\n        values: [DateTime(2001), DateTime(2005), DateTime(2006)],\n        nullableValues: [DateTime(2004), DateTime(2005)],\n        valuesNullable: [DateTime(2004), DateTime(2005), DateTime(2006)],\n        nullableValuesNullable: [null, null, null],\n      );\n      obj5 = DateTimeModel(\n        values: [\n          DateTime(2003),\n          DateTime(2004),\n          DateTime(2005),\n          DateTime(2006),\n          DateTime(2007),\n        ],\n        nullableValues: [\n          null,\n          DateTime(2003),\n          DateTime(2004),\n          DateTime(2005),\n          DateTime(2006),\n        ],\n        valuesNullable: [DateTime(2001)],\n        nullableValuesNullable: null,\n      );\n      obj6 = DateTimeModel(\n        values: [DateTime(0)],\n        nullableValues: [\n          DateTime(0),\n          DateTime(2002),\n          DateTime(2005),\n          DateTime(2006),\n        ],\n        valuesNullable: [DateTime(2004), DateTime(2005), DateTime(0)],\n        nullableValuesNullable: [\n          null,\n          DateTime(0),\n          DateTime(2003),\n          DateTime(2005),\n        ],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.dateTimeModels.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqualSet(\n        isar.dateTimeModels.where().hashEqualTo([\n          DateTime(2001),\n          DateTime(2002),\n          DateTime(2003).toUtc(),\n        ]),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .hashEqualTo([DateTime(2002), DateTime(2004).toUtc()]),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().hashEqualTo([]),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .hashEqualTo([DateTime(2001), DateTime(2005), DateTime(2006)]),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().hashEqualTo(\n          [\n            DateTime(2003),\n            DateTime(2004),\n            DateTime(2005),\n            DateTime(2006),\n            DateTime(2007),\n          ],\n        ),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().hashEqualTo([DateTime(0)]),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().hashEqualTo([DateTime(2042)]),\n        [],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashEqualTo([\n          DateTime(2001),\n          null,\n          DateTime(2003).toUtc(),\n        ]),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashEqualTo([\n          DateTime(2002),\n          DateTime(2003),\n          DateTime(2003).toUtc(),\n        ]),\n        [obj2],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashEqualTo([]),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableHashEqualTo([DateTime(2004), DateTime(2005)]),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashEqualTo([\n          null,\n          DateTime(2003),\n          DateTime(2004),\n          DateTime(2005),\n          DateTime(2006),\n        ]),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashEqualTo([\n          DateTime(0),\n          DateTime(2002),\n          DateTime(2005),\n          DateTime(2006),\n        ]),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().hashNullableEqualTo([DateTime(2001)]),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().hashNullableEqualTo([]),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().hashNullableEqualTo([\n          DateTime(2004),\n          DateTime(2005),\n          DateTime(2006),\n        ]),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .hashNullableEqualTo([DateTime(2004), DateTime(2005), DateTime(0)]),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableHashNullableEqualTo([DateTime(2001), null, null]),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashNullableEqualTo([]),\n        [obj3],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableHashNullableEqualTo([null, null, null]),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashNullableEqualTo([\n          null,\n          DateTime(0),\n          DateTime(2003),\n          DateTime(2005),\n        ]),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(0)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2001)),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2002)),\n        [obj1, obj2],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2003)),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2004)),\n        [obj2, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2005)),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2006)),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2007)),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementEqualTo(DateTime(2042)),\n        [],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableValuesElementEqualTo(DateTime(0)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2001)),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2002)),\n        [obj2, obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2003)),\n        [obj1, obj2, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2004)),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2005)),\n        [obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2006)),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementEqualTo(DateTime(2042)),\n        [],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesNullableElementEqualTo(DateTime(0)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementEqualTo(DateTime(2001)),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementEqualTo(DateTime(2004)),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementEqualTo(DateTime(2005)),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementEqualTo(DateTime(2006)),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementEqualTo(DateTime(2042)),\n        [],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementEqualTo(DateTime(0)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementEqualTo(DateTime(2001)),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementEqualTo(DateTime(2003)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementEqualTo(DateTime(2005)),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementEqualTo(DateTime(2042)),\n        [],\n      );\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableValuesElementIsNull(),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableValuesNullableElementIsNull(),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableValuesElementIsNotNull(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableValuesNullableElementIsNotNull(),\n        [obj1, obj6],\n      );\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementGreaterThan(DateTime(2003)),\n        [obj2, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementGreaterThan(DateTime(2003)),\n        [obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementGreaterThan(DateTime(2003)),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementGreaterThan(DateTime(2003)),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementLessThan()', () async {\n      await qEqualSet(\n        isar.dateTimeModels.where().valuesElementLessThan(DateTime(2003)),\n        [obj1, obj2, obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementLessThan(DateTime(2003)),\n        [obj1, obj2, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementLessThan(DateTime(2003)),\n        [obj1, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesNullableElementLessThan(DateTime(2003)),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.elementBetween()', () async {\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesElementBetween(DateTime(2002), DateTime(2004)),\n        [obj1, obj2, obj5],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .nullableValuesElementBetween(DateTime(2002), DateTime(2004)),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels\n            .where()\n            .valuesNullableElementBetween(DateTime(2002), DateTime(2004)),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableValuesNullableElementBetween(\n              DateTime(2002),\n              DateTime(2004),\n            ),\n        [obj6],\n      );\n    });\n\n    // FIXME/TODO: Should `.lengthXXX()` / `.isEmpty` be implemented on\n    // `values` indexes?\n\n    isarTest('.isNull()', () async {\n      // FIXME: `.isNull()` is not generated on `List<DateTime>?`\n      // await qEqualSet(\n      //   isar.dateTimeModels.where().valuesNullableIsNull(),\n      //   [obj2],\n      // );\n\n      // FIXME: `.isNull()` is not generated on `List<DateTime?>?`\n      // await qEqualSet(\n      //   isar.dateTimeModels.where().nullableValuesNullableIsNull(),\n      //   [obj2, obj5],\n      // );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().hashNullableIsNull(),\n        [obj2],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashNullableIsNull(),\n        [obj2, obj5],\n      );\n    });\n\n    isarTest('.isNotNull()', () async {\n      // FIXME: `.isNotNull()` is not generated on `List<DateTime>?`\n      // await qEqualSet(\n      //   isar.dateTimeModels.where().valuesNullableIsNotNull(),\n      //   [obj1, obj3, obj4, obj5, obj6],\n      // );\n\n      // FIXME: `.isNotNull()` is not generated on List<DateTime?>?\n      // await qEqualSet(\n      //   isar.dateTimeModels.where().nullableValuesNullableIsNotNull(),\n      //   [obj1, obj3, obj4, obj6],\n      // );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().hashNullableIsNotNull(),\n        [obj1, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.dateTimeModels.where().nullableHashNullableIsNotNull(),\n        [obj1, obj3, obj4, obj6],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_date_time_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_date_time_test.g.dart';\n\n@collection\nclass DateTimeModel {\n  DateTimeModel(this.field);\n  Id? id;\n\n  @Index()\n  DateTime? field;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is DateTimeModel && other.field?.toUtc() == field?.toUtc();\n\n  @override\n  String toString() => '{id: $id, field: $field}';\n}\n\nDateTime local(int year, [int month = 1, int day = 1]) {\n  return DateTime(year, month, day);\n}\n\nDateTime utc(int year, [int month = 1, int day = 1]) {\n  return local(year, month, day).toUtc();\n}\n\nvoid main() {\n  group('Where DateTime', () {\n    late Isar isar;\n    late IsarCollection<DateTimeModel> col;\n\n    late DateTimeModel obj1;\n    late DateTimeModel obj2;\n    late DateTimeModel obj3;\n    late DateTimeModel obj4;\n    late DateTimeModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([DateTimeModelSchema]);\n      col = isar.dateTimeModels;\n\n      obj1 = DateTimeModel(local(2010));\n      obj2 = DateTimeModel(local(2020));\n      obj3 = DateTimeModel(local(2010));\n      obj4 = DateTimeModel(utc(2040));\n      objNull = DateTimeModel(null);\n\n      await isar.writeTxn(() async {\n        await isar.dateTimeModels.putAll([obj1, obj2, obj3, obj4, objNull]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(\n        col.where().fieldEqualTo(local(2010)),\n        [obj1, obj3],\n      );\n      await qEqual(\n        col.where().fieldEqualTo(utc(2010)),\n        [obj1, obj3],\n      );\n      await qEqual(col.where().fieldEqualTo(null), [objNull]);\n      await qEqual(col.where().fieldEqualTo(local(2027)), []);\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqual(\n        col.where().fieldNotEqualTo(local(2010)),\n        [objNull, obj2, obj4],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(utc(2010)),\n        [objNull, obj2, obj4],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(null),\n        [obj1, obj3, obj2, obj4],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(local(2027)),\n        [objNull, obj1, obj3, obj2, obj4],\n      );\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(\n        col.where().fieldGreaterThan(local(2010)),\n        [obj2, obj4],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(local(2010), include: true),\n        [obj1, obj3, obj2, obj4],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(null),\n        [obj1, obj3, obj2, obj4],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(null, include: true),\n        [objNull, obj1, obj3, obj2, obj4],\n      );\n      await qEqual(col.where().fieldGreaterThan(local(2050)), []);\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(\n        col.where().fieldLessThan(local(2020)),\n        [objNull, obj1, obj3],\n      );\n      await qEqual(\n        col.where().fieldLessThan(local(2020), include: true),\n        [objNull, obj1, obj3, obj2],\n      );\n      await qEqual(col.where().fieldLessThan(null), []);\n      await qEqual(\n        col.where().fieldLessThan(null, include: true),\n        [objNull],\n      );\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.where().fieldBetween(null, local(2010)),\n        [objNull, obj1, obj3],\n      );\n      await qEqual(\n        col.where().fieldBetween(null, local(2020), includeLower: false),\n        [obj1, obj3, obj2],\n      );\n      await qEqual(\n        col.where().fieldBetween(null, local(2020), includeUpper: false),\n        [objNull, obj1, obj3],\n      );\n      await qEqual(\n        col.where().fieldBetween(local(2030), local(2035)),\n        [],\n      );\n      await qEqual(\n        col.where().fieldBetween(local(2020), local(2000)),\n        [],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.where().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(\n        col.where().fieldIsNotNull(),\n        [obj1, obj3, obj2, obj4],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_float_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_float_list_test.g.dart';\n\n@collection\nclass FloatModel {\n  FloatModel(this.list);\n  Id? id;\n\n  @Index(type: IndexType.value)\n  List<float?>? list;\n\n  @override\n  String toString() {\n    return '{id: $id, list: $list}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is FloatModel && doubleListEquals(other.list, list);\n}\n\nvoid main() {\n  group('Where float list', () {\n    late Isar isar;\n    late IsarCollection<FloatModel> col;\n\n    late FloatModel objEmpty;\n    late FloatModel obj1;\n    late FloatModel obj2;\n    late FloatModel obj3;\n    late FloatModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([FloatModelSchema]);\n      col = isar.floatModels;\n\n      objEmpty = FloatModel([]);\n      obj1 = FloatModel([1.1, 3.3]);\n      obj2 = FloatModel([null]);\n      obj3 = FloatModel([null, -1000]);\n      objNull = FloatModel(null);\n\n      await isar.writeTxn(() async {\n        await col.putAll([objEmpty, obj1, obj2, obj3, objNull]);\n      });\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqualSet(\n        col.where().listElementGreaterThan(1.1),\n        [obj1],\n      );\n      await qEqualSet(col.where().listElementGreaterThan(4), []);\n    });\n\n    isarTest('.elementLessThan()', () async {\n      await qEqualSet(\n        col.where().listElementLessThan(1.1),\n        [obj2, obj3],\n      );\n      await qEqualSet(col.where().listElementLessThan(null), []);\n    });\n\n    isarTest('.anyBetween()', () async {\n      await qEqualSet(col.where().listElementBetween(1, 5), [obj1]);\n      await qEqualSet(col.where().listElementBetween(5, 10), []);\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqualSet(\n        col.where().listElementIsNull(),\n        [obj2, obj3],\n      );\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqualSet(\n        col.where().listElementIsNotNull(),\n        [obj1, obj3],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_float_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_float_test.g.dart';\n\n@collection\nclass FloatModel {\n  FloatModel();\n  Id? id;\n\n  @Index()\n  float? field = 0;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is FloatModel && doubleEquals(field, other.field);\n\n  @override\n  String toString() {\n    return '{id: $id, field: $field}';\n  }\n}\n\nvoid main() {\n  group('Where float', () {\n    late Isar isar;\n    late IsarCollection<FloatModel> col;\n\n    late FloatModel obj0;\n    late FloatModel obj1;\n    late FloatModel obj2;\n    late FloatModel obj3;\n    late FloatModel objInf;\n    late FloatModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([FloatModelSchema]);\n      col = isar.floatModels;\n\n      obj0 = FloatModel()..field = 0;\n      obj1 = FloatModel()..field = 1.1;\n      obj2 = FloatModel()..field = 2.2;\n      obj3 = FloatModel()..field = 3.3;\n      objInf = FloatModel()..field = double.infinity;\n      objNull = FloatModel()..field = null;\n\n      await isar.writeTxn(() async {\n        await col.putAll([objInf, obj0, obj2, obj1, obj3, objNull]);\n      });\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(\n        col.where().fieldGreaterThan(null),\n        [obj0, obj1, obj2, obj3, objInf],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(2.2),\n        [obj3, objInf],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(double.infinity),\n        [],\n      );\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.where().fieldLessThan(1.1), [objNull, obj0]);\n      await qEqual(col.where().fieldLessThan(null), []);\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.where().fieldBetween(1, 3.5),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(col.where().fieldBetween(5, 6), []);\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(col.where().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(col.filter().fieldIsNull(), [objNull]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_id_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_id_test.g.dart';\n\n@collection\nclass IdModel {\n  IdModel();\n\n  Id? id;\n\n  @override\n  String toString() {\n    return '{id: $id}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is IdModel && other.id == id;\n  }\n}\n\nvoid main() {\n  group('Where Id', () {\n    late Isar isar;\n    late IsarCollection<IdModel> col;\n\n    late IdModel obj0;\n    late IdModel obj1;\n    late IdModel obj2;\n    late IdModel obj3;\n\n    setUp(() async {\n      isar = await openTempIsar([IdModelSchema]);\n      col = isar.idModels;\n\n      obj0 = IdModel()..id = 0;\n      obj1 = IdModel()..id = 1;\n      obj2 = IdModel()..id = 2;\n      obj3 = IdModel()..id = 3;\n\n      await isar.writeTxn(() async {\n        await isar.idModels.putAll([obj0, obj2, obj3, obj1]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.where().idEqualTo(2), [obj2]);\n      await qEqual(col.where().idEqualTo(5), []);\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqual(col.where().idNotEqualTo(2), [obj0, obj1, obj3]);\n      await qEqual(\n        col.where().idNotEqualTo(5),\n        [obj0, obj1, obj2, obj3],\n      );\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(col.where().idGreaterThan(2), [obj3]);\n      await qEqual(\n        col.where().idGreaterThan(2, include: true),\n        [obj2, obj3],\n      );\n      await qEqual(col.where().idGreaterThan(3), []);\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.where().idLessThan(1), [obj0]);\n      await qEqual(\n        col.where().idLessThan(1, include: true),\n        [obj0, obj1],\n      );\n      await qEqual(col.where().idLessThan(-1), []);\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.where().idBetween(1, 3),\n        [obj1, obj2, obj3],\n      );\n      await qEqual(\n        col.where().idBetween(1, 3, includeLower: false),\n        [obj2, obj3],\n      );\n      await qEqual(\n        col.where().idBetween(1, 3, includeUpper: false),\n        [obj1, obj2],\n      );\n      await qEqual(\n        col.where().idBetween(1, 3, includeLower: false, includeUpper: false),\n        [obj2],\n      );\n      await qEqual(col.where().idBetween(5, 6), []);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_int_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_int_test.g.dart';\n\n@collection\nclass IntModel {\n  IntModel(this.field);\n  Id? id;\n\n  @Index()\n  short? field = 0;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is IntModel && other.field == field;\n  }\n\n  @override\n  String toString() {\n    return '$field';\n  }\n}\n\nvoid main() {\n  group('Where int', () {\n    late Isar isar;\n    late IsarCollection<IntModel> col;\n\n    late IntModel obj0;\n    late IntModel obj1;\n    late IntModel obj2;\n    late IntModel obj3;\n    late IntModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([IntModelSchema]);\n      col = isar.intModels;\n\n      objNull = IntModel(null);\n      obj0 = IntModel(-1234);\n      obj1 = IntModel(1);\n      obj2 = IntModel(2);\n      obj3 = IntModel(1);\n\n      await isar.writeTxn(() async {\n        await isar.intModels.putAll([obj0, obj1, obj2, obj3, objNull]);\n      });\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(col.where().fieldEqualTo(2), [obj2]);\n      await qEqual(col.where().fieldEqualTo(null), [objNull]);\n      await qEqual(col.where().fieldEqualTo(5), []);\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqual(\n        col.where().fieldNotEqualTo(1),\n        [objNull, obj0, obj2],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(null),\n        [obj0, obj1, obj3, obj2],\n      );\n      await qEqual(\n        col.where().fieldNotEqualTo(5),\n        [objNull, obj0, obj1, obj3, obj2],\n      );\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqual(col.where().fieldGreaterThan(1), [obj2]);\n      await qEqual(\n        col.where().fieldGreaterThan(1, include: true),\n        [obj1, obj3, obj2],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(null),\n        [obj0, obj1, obj3, obj2],\n      );\n      await qEqual(\n        col.where().fieldGreaterThan(null, include: true),\n        [objNull, obj0, obj1, obj3, obj2],\n      );\n      await qEqual(col.where().fieldGreaterThan(4), []);\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqual(col.where().fieldLessThan(1), [objNull, obj0]);\n      await qEqual(col.where().fieldLessThan(null), []);\n      await qEqual(\n        col.where().fieldLessThan(null, include: true),\n        [objNull],\n      );\n    });\n\n    isarTest('.between()', () async {\n      await qEqual(\n        col.where().fieldBetween(1, 2),\n        [obj1, obj3, obj2],\n      );\n      await qEqual(\n        col.where().fieldBetween(1, 2, includeLower: false),\n        [obj2],\n      );\n      await qEqual(\n        col.where().fieldBetween(1, 2, includeUpper: false),\n        [obj1, obj3],\n      );\n      await qEqual(\n        col\n            .where()\n            .fieldBetween(1, 2, includeLower: false, includeUpper: false),\n        [],\n      );\n      await qEqual(\n        col.where().fieldBetween(null, 1),\n        [objNull, obj0, obj1, obj3],\n      );\n      await qEqual(col.where().fieldBetween(5, 6), []);\n    });\n\n    isarTest('.isNull() / .isNotNull()', () async {\n      await qEqual(col.where().fieldIsNull(), [objNull]);\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(\n        col.where().fieldIsNotNull(),\n        [obj0, obj1, obj3, obj2],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_string_list_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_string_list_test.g.dart';\n\n@collection\nclass StringModel {\n  StringModel({\n    required this.values,\n    required this.nullableValues,\n    required this.valuesNullable,\n    required this.nullableValuesNullable,\n  })  : hash = values,\n        nullableHash = nullableValues,\n        hashNullable = valuesNullable,\n        nullableHashNullable = nullableValuesNullable,\n        hashes = values,\n        nullableHashes = nullableValues,\n        hashesNullable = valuesNullable,\n        nullableHashesNullable = nullableValuesNullable;\n\n  Id id = Isar.autoIncrement;\n\n  @Index(type: IndexType.value)\n  List<String> values;\n\n  @Index(type: IndexType.value)\n  List<String?> nullableValues;\n\n  @Index(type: IndexType.value)\n  List<String>? valuesNullable;\n\n  @Index(type: IndexType.value)\n  List<String?>? nullableValuesNullable;\n\n  @Index(type: IndexType.hash)\n  List<String> hash;\n\n  @Index(type: IndexType.hash)\n  List<String?> nullableHash;\n\n  @Index(type: IndexType.hash)\n  List<String>? hashNullable;\n\n  @Index(type: IndexType.hash)\n  List<String?>? nullableHashNullable;\n\n  @Index(type: IndexType.hashElements)\n  List<String> hashes;\n\n  @Index(type: IndexType.hashElements)\n  List<String?> nullableHashes;\n\n  @Index(type: IndexType.hashElements)\n  List<String>? hashesNullable;\n\n  @Index(type: IndexType.hashElements)\n  List<String?>? nullableHashesNullable;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is StringModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          listEquals(values, other.values) &&\n          listEquals(nullableValues, other.nullableValues) &&\n          listEquals(valuesNullable, other.valuesNullable) &&\n          listEquals(nullableValuesNullable, other.nullableValuesNullable) &&\n          listEquals(hash, other.hash) &&\n          listEquals(nullableHash, other.nullableHash) &&\n          listEquals(hashNullable, other.hashNullable) &&\n          listEquals(nullableHashNullable, other.nullableHashNullable) &&\n          listEquals(hashes, other.hashes) &&\n          listEquals(nullableHashes, other.nullableHashes) &&\n          listEquals(hashesNullable, other.hashesNullable) &&\n          listEquals(nullableHashesNullable, other.nullableHashesNullable);\n\n  @override\n  String toString() {\n    return '''StringModel{id: $id, values: $values, nullableValues: $nullableValues, valuesNullable: $valuesNullable, nullableValuesNullable: $nullableValuesNullable, hash: $hash, nullableHash: $nullableHash, hashNullable: $hashNullable, nullableHashNullable: $nullableHashNullable, hashes: $hashes, nullableHashes: $nullableHashes, hashesNullable: $hashesNullable, nullableHashesNullable: $nullableHashesNullable}''';\n  }\n}\n\nvoid main() {\n  group('Where String list', () {\n    late Isar isar;\n\n    late StringModel obj1;\n    late StringModel obj2;\n    late StringModel obj3;\n    late StringModel obj4;\n    late StringModel obj5;\n    late StringModel obj6;\n\n    setUp(() async {\n      isar = await openTempIsar([StringModelSchema]);\n\n      obj1 = StringModel(\n        values: ['strings 1', 'strings 2', 'strings 3'],\n        nullableValues: ['nullable strings 1', null, 'nullable strings 3'],\n        valuesNullable: ['strings nullable 1'],\n        nullableValuesNullable: ['nullable strings nullable 1', null, null],\n      );\n      obj2 = StringModel(\n        values: ['strings 2', 'strings 4'],\n        nullableValues: [\n          'nullable strings 2',\n          'nullable strings 3',\n          'nullable strings 3',\n        ],\n        valuesNullable: null,\n        nullableValuesNullable: null,\n      );\n      obj3 = StringModel(\n        values: [],\n        nullableValues: [],\n        valuesNullable: [],\n        nullableValuesNullable: [],\n      );\n      obj4 = StringModel(\n        values: ['strings 1', 'strings 5', 'strings 6'],\n        nullableValues: ['nullable strings 4', 'nullable strings 5'],\n        valuesNullable: [\n          'strings nullable 4',\n          'strings nullable 5',\n          'strings nullable 6',\n        ],\n        nullableValuesNullable: [null, null, null],\n      );\n      obj5 = StringModel(\n        values: [\n          'strings 3',\n          'strings 4',\n          'strings 5',\n          'strings 6',\n          'strings 7',\n        ],\n        nullableValues: [\n          null,\n          'nullable strings 3',\n          'nullable strings 4',\n          'nullable strings 5',\n          'nullable strings 6',\n        ],\n        valuesNullable: ['strings nullable 1'],\n        nullableValuesNullable: null,\n      );\n      obj6 = StringModel(\n        values: [''],\n        nullableValues: [\n          '',\n          'nullable strings 2',\n          'nullable strings 5',\n          'nullable strings 6',\n        ],\n        valuesNullable: ['strings nullable 4', 'strings nullable 5', ''],\n        nullableValuesNullable: [\n          null,\n          '',\n          'nullable strings nullable 3',\n          'nullable strings nullable 5',\n        ],\n      );\n\n      await isar.tWriteTxn(\n        () => isar.stringModels.tPutAll([obj1, obj2, obj3, obj4, obj5, obj6]),\n      );\n    });\n\n    isarTest('.elementEqualTo()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 1'),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 2'),\n        [obj1, obj2],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 3'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 4'),\n        [obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 5'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 6'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('strings 7'),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementEqualTo('nullable strings 1'),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementEqualTo('nullable strings 2'),\n        [obj2, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementEqualTo('nullable strings 3'),\n        [obj1, obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementEqualTo('nullable strings 4'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementEqualTo('nullable strings 5'),\n        [obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementEqualTo('nullable strings 6'),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesNullableElementEqualTo('strings nullable 1'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesNullableElementEqualTo('strings nullable 4'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesNullableElementEqualTo('strings nullable 5'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesNullableElementEqualTo('strings nullable 6'),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesNullableElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementEqualTo(\n              'nullable strings nullable 1',\n            ),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementEqualTo(\n              'nullable strings nullable 3',\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementEqualTo(\n              'nullable strings nullable 5',\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesNullableElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 1'),\n        [obj1, obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 2'),\n        [obj1, obj2],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 3'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 4'),\n        [obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 5'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 6'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('strings 7'),\n        [obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesElementEqualTo('nullable strings 1'),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesElementEqualTo('nullable strings 2'),\n        [obj2, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesElementEqualTo('nullable strings 3'),\n        [obj1, obj2, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesElementEqualTo('nullable strings 4'),\n        [obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesElementEqualTo('nullable strings 5'),\n        [obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesElementEqualTo('nullable strings 6'),\n        [obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .hashesNullableElementEqualTo('strings nullable 1'),\n        [obj1, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .hashesNullableElementEqualTo('strings nullable 4'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .hashesNullableElementEqualTo('strings nullable 5'),\n        [obj4, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .hashesNullableElementEqualTo('strings nullable 6'),\n        [obj4],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashesNullableElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesNullableElementEqualTo(\n              'nullable strings nullable 1',\n            ),\n        [obj1],\n      );\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesNullableElementEqualTo(\n              'nullable strings nullable 3',\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesNullableElementEqualTo(\n              'nullable strings nullable 5',\n            ),\n        [obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableHashesNullableElementEqualTo('non existing'),\n        [],\n      );\n    });\n\n    isarTest('.elementStartWith()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valuesElementStartsWith('strings'),\n        [obj1, obj2, obj4, obj5],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesElementStartsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesElementStartsWith('nullable'),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementStartsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valuesNullableElementStartsWith('strings'),\n        [obj1, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valuesNullableElementEqualTo('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesNullableElementStartsWith('nullable'),\n        [obj1, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesNullableElementStartsWith('non existing'),\n        [],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().hashesNullableElementEqualTo('non existing'),\n        [],\n      );\n    });\n\n    isarTest('.elementIsNull()', () async {\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesElementIsNull(),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementIsNull(),\n        [obj1, obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesElementIsNull(),\n        [obj1, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesNullableElementIsNull(),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.elementIsNotNull()', () async {\n      await qEqualSet(\n        isar.stringModels.filter().nullableValuesElementIsNotNull(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementIsNotNull(),\n        [obj1, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.filter().nullableHashesElementIsNotNull(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableHashesNullableElementIsNotNull(),\n        [obj1, obj6],\n      );\n    });\n\n    isarTest('.elementGreaterThan()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valuesElementGreaterThan('strings 3'),\n        [obj2, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementGreaterThan('nullable strings 3'),\n        [obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesNullableElementGreaterThan('strings nullable 3'),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementGreaterThan(\n              'nullable strings nullable 3',\n            ),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementLessThan()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valuesElementLessThan('strings 3'),\n        [obj1, obj2, obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .nullableValuesElementLessThan('nullable strings 3'),\n        [obj1, obj2, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesNullableElementLessThan('strings nullable 3'),\n        [obj1, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementLessThan(\n              'nullable strings nullable 3',\n            ),\n        [obj1, obj4, obj6],\n      );\n    });\n\n    isarTest('.elementBetween()', () async {\n      await qEqualSet(\n        isar.stringModels\n            .where()\n            .valuesElementBetween('strings 2', 'strings 4'),\n        [obj1, obj2, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesElementBetween(\n              'nullable strings 2',\n              'nullable strings 4',\n            ),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valuesNullableElementBetween(\n              'strings nullable 2',\n              'strings nullable 4',\n            ),\n        [obj4, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementBetween(\n              'nullable strings nullable 2',\n              'nullable strings nullable 4',\n            ),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementIsEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valuesElementIsEmpty(),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesElementIsEmpty(),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valuesNullableElementIsEmpty(),\n        [obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementIsEmpty(),\n        [obj6],\n      );\n    });\n\n    isarTest('.elementIsNotEmpty()', () async {\n      // FIXME\n      /*await qEqualSet(\n        isar.stringModels.where().valuesElementIsNotEmpty(),\n        [obj1, obj2, obj4, obj5],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesElementIsNotEmpty(),\n        [obj1, obj2, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valuesNullableElementIsNotEmpty(),\n        [obj1, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableValuesNullableElementIsNotEmpty(),\n        [obj1, obj6],\n      );*/\n    });\n\n    isarTest('.isNull()', () async {\n      // FIXME\n      /*await qEqualSet(\n        isar.stringModels.where().hashNullableIsNull(),\n        [obj2],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().nullableHashNullableIsNull(),\n        [obj2, obj5],\n      );*/\n    });\n\n    isarTest('.isNotNull()', () async {\n      // FIXME: Returning wrong values\n      /*await qEqualSet(\n        isar.stringModels.where().hashNullableIsNotNull(),\n        [obj1, obj3, obj4, obj5, obj6],\n      );*/\n\n      // FIXME: Returning wrong values\n      /*await qEqualSet(\n        isar.stringModels.where().nullableHashNullableIsNotNull(),\n        [obj1, obj3, obj4, obj6],\n      );*/\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/index/where_string_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_string_test.g.dart';\n\n@collection\nclass StringModel {\n  StringModel(this.value) : hash = value;\n\n  Id id = Isar.autoIncrement;\n\n  @Index(type: IndexType.value)\n  String? value;\n\n  @Index(type: IndexType.hash)\n  String? hash;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is StringModel && value == other.value;\n\n  @override\n  String toString() {\n    return 'StringModel{id: $id, value: $value}';\n  }\n}\n\nvoid main() {\n  group('Where String', () {\n    late Isar isar;\n\n    late StringModel objEmpty;\n    late StringModel obj1;\n    late StringModel obj2;\n    late StringModel obj3;\n    late StringModel obj4;\n    late StringModel obj5;\n    late StringModel obj6;\n    late StringModel objNull;\n\n    setUp(() async {\n      isar = await openTempIsar([StringModelSchema]);\n\n      objEmpty = StringModel('');\n      obj1 = StringModel('string 1');\n      obj2 = StringModel('string 2');\n      obj3 = StringModel('string 3');\n      obj4 = StringModel('string 4');\n      obj5 = StringModel('string 5');\n      obj6 = StringModel('string 5');\n      objNull = StringModel(null);\n\n      await isar.writeTxn(\n        () async => isar.stringModels.tPutAll([\n          objEmpty,\n          obj1,\n          obj2,\n          obj3,\n          obj4,\n          obj5,\n          obj6,\n          objNull,\n        ]),\n      );\n    });\n\n    isarTest('.equalTo()', () async {\n      await qEqual(\n        isar.stringModels.where().valueEqualTo('string 2'),\n        [obj2],\n      );\n      await qEqual(\n        isar.stringModels.where().valueEqualTo(null),\n        [objNull],\n      );\n      await qEqual(\n        isar.stringModels.where().valueEqualTo('string 6'),\n        [],\n      );\n      await qEqual(\n        isar.stringModels.where().valueEqualTo(''),\n        [objEmpty],\n      );\n      await qEqual(\n        isar.stringModels.where().valueEqualTo('non existing'),\n        [],\n      );\n\n      await qEqual(\n        isar.stringModels.where().hashEqualTo('string 2'),\n        [obj2],\n      );\n      await qEqual(\n        isar.stringModels.where().hashEqualTo(null),\n        [objNull],\n      );\n      await qEqual(\n        isar.stringModels.where().hashEqualTo('string 6'),\n        [],\n      );\n      await qEqual(\n        isar.stringModels.where().hashEqualTo(''),\n        [objEmpty],\n      );\n      await qEqual(\n        isar.stringModels.where().hashEqualTo('non existing'),\n        [],\n      );\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valueNotEqualTo('string 2'),\n        [objEmpty, obj1, obj3, obj4, obj5, obj6, objNull],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valueNotEqualTo(null),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valueNotEqualTo('string 6'),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6, objNull],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valueNotEqualTo(''),\n        [obj1, obj2, obj3, obj4, obj5, obj6, objNull],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valueNotEqualTo('non existing'),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6, objNull],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().hashNotEqualTo('string 2'),\n        [objEmpty, obj1, obj3, obj4, obj5, obj6, objNull],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashNotEqualTo(null),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashNotEqualTo('string 6'),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6, objNull],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashNotEqualTo(''),\n        [obj1, obj2, obj3, obj4, obj5, obj6, objNull],\n      );\n      await qEqualSet(\n        isar.stringModels.where().hashNotEqualTo('non existing'),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6, objNull],\n      );\n    });\n\n    isarTest('.isNull()', () async {\n      await qEqual(\n        isar.stringModels.where().valueIsNull(),\n        [objNull],\n      );\n\n      await qEqual(\n        isar.stringModels.where().hashIsNull(),\n        [objNull],\n      );\n    });\n\n    isarTest('.startsWith()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valueStartsWith('string'),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(\n        isar.stringModels.where().valueStartsWith(''),\n        [objEmpty, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n      await qEqualSet(isar.stringModels.where().valueStartsWith('S'), {});\n    });\n\n    isarTest('.greaterThan()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valueGreaterThan('string 0'),\n        [obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueGreaterThan('string 1'),\n        [obj2, obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueGreaterThan('string 2'),\n        [obj3, obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueGreaterThan('string 3'),\n        [obj4, obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueGreaterThan('string 4'),\n        [obj5, obj6],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueGreaterThan('string 5'),\n        [],\n      );\n    });\n\n    isarTest('.lessThan()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 0'),\n        [objEmpty, objNull],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 1'),\n        [objEmpty, objNull],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 2'),\n        [objEmpty, objNull, obj1],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 3'),\n        [objEmpty, objNull, obj1, obj2],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 4'),\n        [objEmpty, objNull, obj1, obj2, obj3],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 5'),\n        [objEmpty, objNull, obj1, obj2, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueLessThan('string 6'),\n        [objEmpty, objNull, obj1, obj2, obj3, obj4, obj5, obj6],\n      );\n    });\n\n    isarTest('.between()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valueBetween('string 2', 'string 4'),\n        [obj2, obj3, obj4],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueBetween('', 'string 2'),\n        [objEmpty, obj1, obj2],\n      );\n\n      await qEqualSet(\n        isar.stringModels.where().valueBetween(\n              '',\n              'string 2',\n              includeLower: false,\n              includeUpper: false,\n            ),\n        [obj1],\n      );\n    });\n\n    isarTest('.isEmpty()', () async {\n      await qEqualSet(\n        isar.stringModels.where().valueIsEmpty(),\n        [objEmpty],\n      );\n    });\n\n    isarTest('.isNotEmpty()', () async {\n      // FIXME: returns every values + 2 times the empty value\n      // returns [objNull, objEmpty, objEmpty, obj1, obj2, obj3, obj4, obj5]\n      // await qEqualSet(\n      //   isar.stringModels.where().valueIsNotEmpty(),\n      //   [obj1, obj2, obj3, obj4, obj5, obj6],\n      // );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/inheritance_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'inheritance_test.g.dart';\n\nabstract class BaseModel {\n  BaseModel({\n    required this.name,\n    required this.nickname,\n  });\n\n  Id identifier = Isar.autoIncrement;\n\n  @Index()\n  int get nameHash => name.hashCode;\n\n  final String name;\n\n  final String nickname;\n\n  // ignore:unused_field\n  final float _privateProperty = 0;\n\n  @Ignore()\n  final short ignoredProperty = 42;\n\n  final link = IsarLink<InheritingModel>();\n}\n\n@collection\nclass InheritingModel extends BaseModel {\n  InheritingModel({\n    required super.name,\n    required super.nickname,\n    required this.age,\n  });\n\n  final int age;\n\n  @override\n  String toString() {\n    return 'InheritingModel{name: $name, nameHash: $nameHash, nickname: '\n        '$nickname, age: $age}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is InheritingModel &&\n          runtimeType == other.runtimeType &&\n          name == other.name &&\n          nameHash == other.nameHash &&\n          nickname == other.nickname &&\n          age == other.age;\n}\n\n@Collection(inheritance: false)\nclass NonInheritingModel extends BaseModel {\n  NonInheritingModel({\n    required this.age,\n    required this.nickname,\n  }) : super(name: '', nickname: nickname);\n\n  Id id = Isar.autoIncrement;\n\n  final int age;\n\n  @override\n  // ignore:overridden_fields\n  final String nickname;\n\n  @override\n  String toString() {\n    return 'NonInheritingModel{id: $id, age: $age, nickname: $nickname}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is NonInheritingModel &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          age == other.age &&\n          nickname == other.nickname;\n}\n\nvoid main() {\n  group('Inheritance', () {\n    late Isar isar;\n\n    late InheritingModel inheritingObj0;\n    late InheritingModel inheritingObj1;\n    late InheritingModel inheritingObj2;\n    late InheritingModel inheritingObj3;\n    late InheritingModel inheritingObj4;\n    late InheritingModel inheritingObj5;\n\n    late NonInheritingModel nonInheritingObj0;\n    late NonInheritingModel nonInheritingObj1;\n    late NonInheritingModel nonInheritingObj2;\n\n    setUp(() async {\n      isar = await openTempIsar([\n        InheritingModelSchema,\n        NonInheritingModelSchema,\n      ]);\n\n      inheritingObj0 = InheritingModel(\n        name: 'inheriting obj0',\n        nickname: 'obj0',\n        age: 42,\n      );\n      inheritingObj1 = InheritingModel(\n        name: 'inheriting obj1',\n        nickname: 'obj1',\n        age: 2,\n      );\n      inheritingObj2 = InheritingModel(\n        name: 'inheriting obj2',\n        nickname: 'obj2',\n        age: 22,\n      );\n      inheritingObj3 = InheritingModel(\n        name: 'inheriting obj3',\n        nickname: 'obj3',\n        age: 54,\n      );\n      inheritingObj4 = InheritingModel(\n        name: 'inheriting obj4',\n        nickname: 'obj4',\n        age: 24,\n      );\n      inheritingObj5 = InheritingModel(\n        name: 'inheriting obj5',\n        nickname: 'obj5',\n        age: 31,\n      );\n\n      nonInheritingObj0 = NonInheritingModel(age: 22, nickname: 'non-obj0');\n      nonInheritingObj1 = NonInheritingModel(age: 56, nickname: 'non-obj1');\n      nonInheritingObj2 = NonInheritingModel(age: 65, nickname: 'non-obj2');\n\n      await isar.tWriteTxn(() async {\n        await isar.inheritingModels.tPutAll([\n          inheritingObj0,\n          inheritingObj1,\n          inheritingObj2,\n          inheritingObj3,\n          inheritingObj4,\n          inheritingObj5,\n        ]);\n        await isar.nonInheritingModels.tPutAll([\n          nonInheritingObj0,\n          nonInheritingObj1,\n          nonInheritingObj2,\n        ]);\n      });\n\n      inheritingObj0.link.value = inheritingObj1;\n      inheritingObj2.link.value = inheritingObj0;\n      inheritingObj5.link.value = inheritingObj3;\n\n      await isar.tWriteTxn(() async {\n        await inheritingObj0.link.tSave();\n        await inheritingObj2.link.tSave();\n        await inheritingObj5.link.tSave();\n      });\n    });\n\n    /*test('Validate inheritance model properties', () {\n      expect(InheritingModelSchema.idName, 'identifier');\n      expect(InheritingModelSchema.propertyIds.containsKey('nameHash'), true);\n      expect(InheritingModelSchema.propertyIds.containsKey('name'), true);\n      expect(InheritingModelSchema.propertyIds.containsKey('nickname'), true);\n      expect(InheritingModelSchema.propertyIds.containsKey('age'), true);\n      expect(\n        InheritingModelSchema.propertyIds.containsKey('_privateProperty'),\n        false,\n      );\n      expect(\n        InheritingModelSchema.propertyIds.containsKey('ignoredProperty'),\n        false,\n      );\n      expect(InheritingModelSchema.linkIds.containsKey('link'), true);\n      expect(InheritingModelSchema.indexIds.containsKey('nameHash'), true);\n    });\n\n    test('Validation non inheritance model properties', () {\n      expect(NonInheritingModelSchema.idName, 'id');\n      expect(\n        NonInheritingModelSchema.propertyIds.containsKey('nameHash'),\n        false,\n      );\n      expect(NonInheritingModelSchema.propertyIds.containsKey('name'), false);\n      expect(\n        NonInheritingModelSchema.propertyIds.containsKey('nickname'),\n        true,\n      );\n      expect(NonInheritingModelSchema.propertyIds.containsKey('age'), true);\n      expect(\n        NonInheritingModelSchema.propertyIds.containsKey('_privateProperty'),\n        false,\n      );\n      expect(\n        NonInheritingModelSchema.propertyIds.containsKey('ignoredProperty'),\n        false,\n      );\n      expect(NonInheritingModelSchema.linkIds.containsKey('link'), false);\n      expect(NonInheritingModelSchema.indexIds.containsKey('nameHash'), false);\n    });*/\n\n    isarTest('Query model with inheritance', () async {\n      await qEqualSet(\n        isar.inheritingModels.filter().nameContains('1').or().nameContains('4'),\n        {inheritingObj1, inheritingObj4},\n      );\n\n      await qEqualSet(\n        isar.inheritingModels\n            .filter()\n            .nicknameContains('3')\n            .or()\n            .nicknameContains('0'),\n        {inheritingObj3, inheritingObj0},\n      );\n\n      await qEqualSet(\n        isar.inheritingModels.filter().ageLessThan(40),\n        {inheritingObj1, inheritingObj2, inheritingObj4, inheritingObj5},\n      );\n\n      await qEqualSet(\n        isar.inheritingModels\n            .filter()\n            .link((q) => q.nameEqualTo(inheritingObj3.name)),\n        {inheritingObj5},\n      );\n\n      await qEqualSet(\n        isar.inheritingModels\n            .filter()\n            .link((q) => q.nameEqualTo(inheritingObj4.name)),\n        {},\n      );\n    });\n\n    isarTest('Query model with inherited index', () async {\n      await qEqualSet(\n        isar.inheritingModels\n            .where()\n            .nameHashEqualTo(inheritingObj1.name.hashCode)\n            .or()\n            .nameHashEqualTo(inheritingObj0.name.hashCode),\n        {inheritingObj1, inheritingObj0},\n      );\n\n      await qEqualSet(\n        isar.inheritingModels\n            .where()\n            .nameHashNotEqualTo(inheritingObj1.nameHash)\n            .nameHashProperty(),\n        {\n          inheritingObj0.nameHash,\n          inheritingObj2.nameHash,\n          inheritingObj3.nameHash,\n          inheritingObj4.nameHash,\n          inheritingObj5.nameHash,\n        },\n      );\n\n      await qEqualSet(\n        isar.inheritingModels.where().anyNameHash(),\n        {\n          inheritingObj0,\n          inheritingObj1,\n          inheritingObj2,\n          inheritingObj3,\n          inheritingObj4,\n          inheritingObj5,\n        },\n      );\n    });\n\n    isarTest('Query model without inheritance', () async {\n      await qEqualSet(\n        isar.nonInheritingModels.filter().ageBetween(30, 60),\n        {nonInheritingObj1},\n      );\n\n      await qEqualSet(\n        isar.nonInheritingModels\n            .filter()\n            .nicknameContains('obj1')\n            .or()\n            .nicknameContains('obj0'),\n        {nonInheritingObj1, nonInheritingObj0},\n      );\n\n      await qEqualSet(\n        isar.nonInheritingModels.filter().idGreaterThan(1),\n        {nonInheritingObj1, nonInheritingObj2},\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/instance_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'instance_test.g.dart';\n\n@collection\nclass Model {\n  Id? id;\n\n  @Index()\n  String? value;\n\n  @override\n  String toString() {\n    return '{id: $id, value: $value}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(dynamic other) {\n    return other is Model && other.id == id && other.value == value;\n  }\n}\n\nvoid main() {\n  group('Instance test', () {\n    isarTest('persists auto increment', () async {\n      var isar = await openTempIsar([ModelSchema]);\n      final isarName = isar.name;\n\n      final obj1 = Model()..value = 'M1';\n      await isar.tWriteTxn(() async {\n        await isar.models.tPut(obj1);\n      });\n      expect(obj1.id, 1);\n      expect(await isar.models.tGet(obj1.id!), obj1);\n\n      expect(await isar.close(), true);\n      isar = await openTempIsar(\n        [ModelSchema],\n        name: isarName,\n      );\n\n      final obj2 = Model()..value = 'M2';\n      final obj3 = Model()\n        ..value = 'M3'\n        ..id = 20;\n      await isar.tWriteTxn(() async {\n        await isar.models.tPutAll([obj2, obj3]);\n      });\n      expect(obj2.id, 2);\n      expect(obj3.id, 20);\n      expect(await isar.models.tGet(obj2.id!), obj2);\n      expect(await isar.models.tGet(obj3.id!), obj3);\n\n      expect(await isar.close(), true);\n      isar = await openTempIsar([ModelSchema], name: isarName);\n\n      final obj4 = Model()..value = 'M4';\n      await isar.tWriteTxn(() async {\n        await isar.models.tPut(obj4);\n      });\n      expect(obj4.id, 21);\n      await qEqual(isar.models.where(), [obj1, obj2, obj3, obj4]);\n    });\n\n    isarTest('Prevents usage of closed collection', () async {\n      final isar = await openTempIsar([ModelSchema]);\n\n      expect(await isar.close(deleteFromDisk: true), true);\n\n      await expectLater(\n        () => isar.models.tGet(1),\n        throwsIsarError('already been closed'),\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/isolate_test.dart",
    "content": "@TestOn('vm')\n\nimport 'dart:isolate';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'isolate_test.g.dart';\n\n@collection\nclass TestModel {\n  Id? id;\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is TestModel && other.id == id && other.value == value;\n  }\n}\n\nfinal TestModel _obj1 = TestModel()\n  ..id = 1\n  ..value = 'Model 1';\nfinal TestModel _obj2 = TestModel()\n  ..id = 2\n  ..value = 'Model 2';\nfinal TestModel _obj3 = TestModel()\n  ..id = 3\n  ..value = 'Model 3';\n\nFuture<void> _isolateFunc(SendPort port) async {\n  final isar = await openTempIsar(\n    [TestModelSchema],\n    name: 'test',\n    directory: '',\n  );\n\n  final current = isar.testModels.where().findAllSync();\n  assert(current[0] == _obj1 && current[1] == _obj2, 'Did not find objects');\n\n  isar.writeTxnSync(() {\n    isar.testModels.deleteSync(2);\n    isar.testModels.putSync(_obj3);\n  });\n\n  assert(!(await isar.close()), 'Instance was closed incorrectly');\n\n  port.send(true);\n}\n\nvoid main() {\n  isarTest('Isolate test', () async {\n    final isar = await openTempIsar([TestModelSchema], name: 'test');\n\n    await isar.tWriteTxn(() async {\n      await isar.testModels.tPutAll([_obj1, _obj2]);\n    });\n\n    final port = ReceivePort();\n    await Isolate.spawn(\n      _isolateFunc,\n      port.sendPort,\n      onError: port.sendPort,\n    );\n    final result = await port.first;\n    expect(result, true);\n\n    await qEqual(isar.testModels.where(), [_obj1, _obj3]);\n\n    expect(await isar.close(deleteFromDisk: true), true);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/json_test.dart",
    "content": "import 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('JSON', () {\n    late Isar isar;\n    setUp(() async {\n      isar = await openTempIsar([TweetSchema]);\n    });\n\n    test('Import / Export', () async {\n      await isar.tWriteTxn(() async {\n        await isar.tweets.tImportJson([tweetJson]);\n      });\n\n      expect(await isar.tweets.where().exportJson(), [tweetJson]);\n    });\n\n    test('Import / Export raw', () async {\n      final bytes = JsonUtf8Encoder().convert([tweetJson]);\n      await isar.tWriteTxn(() async {\n        await isar.tweets.tImportJsonRaw(Uint8List.fromList(bytes));\n      });\n\n      await isar.tweets.where().tExportJsonRaw((qBytes) {\n        expect(bytes, qBytes);\n      });\n    });\n\n    test('Import raw malformed', () async {\n      final i1 = isar.tWriteTxn(() async {\n        await isar.tweets.tImportJsonRaw(Uint8List(0));\n      });\n      await expectLater(() => i1, throwsIsarError());\n\n      final i2 = isar.tWriteTxn(() async {\n        final bytes = JsonUtf8Encoder().convert({});\n        await isar.tweets.tImportJsonRaw(Uint8List.fromList(bytes));\n      });\n      await expectLater(() => i2, throwsIsarError());\n    });\n  });\n}\n\nconst tweetJson = {\n  'coordinates': {\n    'coordinates': [14.48532271, 40.63070878],\n    'type': 'Point'\n  },\n  'createdAt': 1432502262000000,\n  'currentUserRetweet': null,\n  'displayTextRange': [0, 123],\n  'entities': {\n    'hashtags': [\n      {\n        'indices': [42, 51],\n        'text': 'foodporn'\n      },\n      {\n        'indices': [52, 64],\n        'text': 'gialloblogs'\n      },\n      {\n        'indices': [65, 87],\n        'text': 'RicetteBloggerRiunite'\n      },\n      {\n        'indices': [88, 97],\n        'text': 'Expo2015'\n      }\n    ],\n    'media': [\n      {\n        'additionalMediaInfo': null,\n        'displayUrl': 'pic.twitter.com/yFgRfzL6DF',\n        'expandedUrl':\n            'https://twitter.com/caterinaboagno/status/602408908561473536/photo/1',\n        'idStr': '602408906787291136',\n        'indices': [98, 120],\n        'mediaUrl': 'http://pbs.twimg.com/media/CFwvtYKWgAAaOV9.jpg',\n        'mediaUrlHttps': 'https://pbs.twimg.com/media/CFwvtYKWgAAaOV9.jpg',\n        'sizes': {\n          'large': {'h': 450, 'resize': 'fit', 'w': 600},\n          'medium': {'h': 450, 'resize': 'fit', 'w': 600},\n          'small': {'h': 450, 'resize': 'fit', 'w': 600},\n          'thumb': {'h': 150, 'resize': 'crop', 'w': 150}\n        },\n        'sourceStatusIdStr': '602408908561473536',\n        'type': 'photo',\n        'url': 'http://t.co/yFgRfzL6DF',\n        'videoInfo': null\n      }\n    ],\n    'polls': null,\n    'symbols': <dynamic>[],\n    'urls': [\n      {\n        'displayUrl': 'blog.giallozafferano.it/lacucinadikaty…',\n        'expandedUrl':\n            'http://blog.giallozafferano.it/lacucinadikaty/cheesecake-frutta/',\n        'indices': [18, 40],\n        'url': 'http://t.co/S8yyMcL62d'\n      }\n    ],\n    'userMentions': [\n      {\n        'idStr': '993103729',\n        'indices': [1, 16],\n        'name': 'La cucina di katy',\n        'screenName': 'caterinaboagno'\n      }\n    ]\n  },\n  'extendedEntities': {\n    'hashtags': null,\n    'media': [\n      {\n        'additionalMediaInfo': null,\n        'displayUrl': 'pic.twitter.com/yFgRfzL6DF',\n        'expandedUrl':\n            'https://twitter.com/caterinaboagno/status/602408908561473536/photo/1',\n        'idStr': '602408906787291136',\n        'indices': [98, 120],\n        'mediaUrl': 'http://pbs.twimg.com/media/CFwvtYKWgAAaOV9.jpg',\n        'mediaUrlHttps': 'https://pbs.twimg.com/media/CFwvtYKWgAAaOV9.jpg',\n        'sizes': {\n          'large': {'h': 450, 'resize': 'fit', 'w': 600},\n          'medium': {'h': 450, 'resize': 'fit', 'w': 600},\n          'small': {'h': 450, 'resize': 'fit', 'w': 600},\n          'thumb': {'h': 150, 'resize': 'crop', 'w': 150}\n        },\n        'sourceStatusIdStr': '602408908561473536',\n        'type': 'photo',\n        'url': 'http://t.co/yFgRfzL6DF',\n        'videoInfo': null\n      }\n    ],\n    'polls': null,\n    'symbols': null,\n    'urls': null,\n    'userMentions': null\n  },\n  'favoriteCount': 2,\n  'favorited': false,\n  'fullText':\n      '\"@caterinaboagno: http://t.co/S8yyMcL62d\\n\\n#foodporn #gialloblogs #RicetteBloggerRiunite #Expo2015 http://t.co/yFgRfzL6DF\" 😍',\n  'idStr': '602584278883553280',\n  'inReplyToScreenName': 'caterinaboagno',\n  'inReplyToStatusIdStr': '602408908561473536',\n  'inReplyToUserIdStr': '993103729',\n  'isQuoteStatus': false,\n  'isarId': 372526,\n  'lang': 'und',\n  'place': {\n    'country': 'Italien',\n    'countryCode': 'IT',\n    'fullName': 'Positano, Kampanien',\n    'id': 'ab6034f6c3eb69c8',\n    'name': 'Positano',\n    'placeType': 'city',\n    'url': 'https://api.twitter.com/1.1/geo/id/ab6034f6c3eb69c8.json'\n  },\n  'possiblySensitive': false,\n  'possiblySensitiveAppealable': null,\n  'quoteCount': null,\n  'quotedStatusIdStr': null,\n  'quotedStatusPermalink': null,\n  'replyCount': null,\n  'retweetCount': 3,\n  'retweeted': false,\n  'source':\n      '<a href=\"http://www.twitter.com\" rel=\"nofollow\">Twitter for Windows Phone</a>',\n  'truncated': false,\n  'user': {\n    'createdAt': 1409213170000000,\n    'defaultProfile': true,\n    'defaultProfileImage': false,\n    'description': 'Born&Living in Positano ❤',\n    'entities': {\n      'description': {'urls': <dynamic>[]},\n      'url': null\n    },\n    'favoritesCount': null,\n    'followersCount': 1671,\n    'friendsCount': 2293,\n    'idStr': '2753148281',\n    'listedCount': 32,\n    'location': 'Positano',\n    'name': 'Rosa Cinque',\n    'profileBannerUrl':\n        'https://pbs.twimg.com/profile_banners/2753148281/1519746619',\n    'profileImageUrlHttps':\n        'https://pbs.twimg.com/profile_images/784521636955451393/Xwp0rcPc_normal.jpg',\n    'protected': false,\n    'screenName': 'rosa_cinque',\n    'statusesCount': 4901,\n    'url': null,\n    'verified': false,\n    'withheldInCountries': <dynamic>[],\n    'withheldScope': null\n  }\n};\n"
  },
  {
    "path": "packages/isar_test/test/link_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'link_test.g.dart';\n\n@collection\nclass LinkModelA {\n  LinkModelA();\n\n  LinkModelA.name(this.name);\n\n  Id? id;\n\n  late String name;\n\n  final selfLink = IsarLink<LinkModelA>();\n\n  final otherLink = IsarLink<LinkModelB>();\n\n  final selfLinks = IsarLinks<LinkModelA>();\n\n  final otherLinks = IsarLinks<LinkModelB>();\n\n  @Backlink(to: 'selfLink')\n  final selfLinkBacklink = IsarLinks<LinkModelA>();\n\n  @Backlink(to: 'selfLinks')\n  final selfLinksBacklink = IsarLinks<LinkModelA>();\n\n  @override\n  String toString() {\n    return 'LinkModelA($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelA && id == other.id && other.name == name;\n  }\n}\n\n@collection\nclass LinkModelB {\n  LinkModelB();\n\n  LinkModelB.name(this.name);\n\n  Id? id;\n\n  late String name;\n\n  @Backlink(to: 'otherLink')\n  final linkBacklinks = IsarLinks<LinkModelA>();\n\n  @Backlink(to: 'otherLinks')\n  IsarLinks<LinkModelA> linksBacklinks = IsarLinks<LinkModelA>();\n\n  @override\n  String toString() {\n    return 'LinkModelB($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelB && id == other.id && other.name == name;\n  }\n}\n\nvoid main() {\n  group('Links', () {\n    late Isar isar;\n    late IsarCollection<LinkModelA> linksA;\n    late IsarCollection<LinkModelB> linksB;\n\n    late LinkModelA objA1;\n    late LinkModelA objA2;\n    late LinkModelA objA3;\n\n    late LinkModelB objB1;\n    late LinkModelB objB2;\n    late LinkModelB objB3;\n\n    setUp(() async {\n      isar = await openTempIsar([LinkModelASchema, LinkModelBSchema]);\n      linksA = isar.linkModelAs;\n      linksB = isar.linkModelBs;\n\n      objA1 = LinkModelA.name('modelA1');\n      objA2 = LinkModelA.name('modelA2');\n      objA3 = LinkModelA.name('modelA3');\n\n      objB1 = LinkModelB.name('modelB1');\n      objB2 = LinkModelB.name('modelB2');\n      objB3 = LinkModelB.name('modelB3');\n    });\n\n    group('self link', () {\n      isarTest('save link manually', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        expect(newA1.selfLink.value, objA2);\n      });\n\n      isarTest('multiple save', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() async {\n          await Future.wait([\n            for (int i = 0; i < 100; i++) objA1.selfLink.tSave(),\n          ]);\n        });\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        expect(newA1.selfLink.value, objA2);\n      });\n\n      isarTest('.load() / .save()', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        expect(newA1.selfLink.value, objA2);\n\n        objA1.selfLink.value = null;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        await newA1.selfLink.tLoad();\n        expect(newA1.selfLink.value, null);\n      });\n\n      isarTest('delete source', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        await isar.tWriteTxn(() => linksA.tDelete(objA1.id!));\n\n        final newA2 = await linksA.tGet(objA2.id!);\n        await newA2!.selfLinkBacklink.tLoad();\n        expect(newA2.selfLinkBacklink, const <LinkModelA>[]);\n      });\n\n      isarTest('delete target', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        await isar.tWriteTxn(() => linksA.tDelete(objA2.id!));\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        expect(newA1.selfLink.value, null);\n      });\n\n      isarTest('delete with same obj link', () async {\n        await isar.tWriteTxn(() => linksA.tPut(objA1));\n\n        objA1.selfLink.value = objA1;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        await isar.tWriteTxn(() => linksA.tDelete(objA1.id!));\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        expect(newA1, null);\n      });\n\n      isarTest('reset loaded link', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        expect(newA1.selfLink.value, objA2);\n\n        await isar.tWriteTxn(() => objA1.selfLink.tReset());\n\n        final newestA1 = await linksA.tGet(objA1.id!);\n        await newestA1!.selfLink.tLoad();\n        expect(newestA1.selfLink.value, null);\n      });\n\n      isarTest('reset unloaded link', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await isar.tWriteTxn(() => newA1!.selfLink.tReset());\n\n        final newestA1 = await linksA.tGet(objA1.id!);\n        await newestA1!.selfLink.tLoad();\n        expect(newestA1.selfLink.value, null);\n      });\n\n      isarTest('multiple updates', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        objA1.selfLink.value = null;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n        await isar.tWriteTxn(() => objA1.selfLink.tReset());\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        expect(newA1.selfLink.value, objA2);\n      });\n\n      isarTest('nested links', () async {\n        await isar.tWriteTxn(() => linksA.tPut(objA1));\n\n        objA1.selfLink.value = objA1;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLink.tLoad();\n        await newA1.selfLink.value?.selfLink.tLoad();\n        await newA1.selfLink.value?.selfLink.value?.selfLink.tLoad();\n\n        expect(newA1, objA1);\n        expect(newA1.selfLink.value, objA1);\n        expect(newA1.selfLink.value?.selfLink.value, objA1);\n        expect(newA1.selfLink.value?.selfLink.value?.selfLink.value, objA1);\n      });\n\n      isarTest('backlink', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2]));\n\n        objA1.selfLink.value = objA2;\n        await isar.tWriteTxn(() => objA1.selfLink.tSave());\n\n        final newA2 = await linksA.tGet(objA2.id!);\n        await newA2!.selfLinkBacklink.tLoad();\n        expect(newA2.selfLinkBacklink, [objA1]);\n\n        newA2.selfLink.value = newA2;\n        await isar.tWriteTxn(newA2.selfLink.tSave);\n\n        final newestA2 = await linksA.tGet(objA2.id!);\n        await newestA2!.selfLinkBacklink.tLoad();\n        expect(newestA2.selfLinkBacklink, {objA1, objA2});\n      });\n    });\n\n    group('other link', () {\n      isarTest('save link', () async {\n        await isar.tWriteTxn(() async {\n          await linksA.tPut(objA1);\n          await linksB.tPut(objB1);\n        });\n\n        objA1.otherLink.value = objB1;\n        await isar.tWriteTxn(() => objA1.otherLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.otherLink.tLoad();\n        expect(newA1.otherLink.value, objB1);\n      });\n\n      isarTest('.load() / .save()', () async {\n        await isar.tWriteTxn(() async {\n          await linksA.tPut(objA1);\n          await linksB.tPut(objB1);\n        });\n\n        objA1.otherLink.value = objB1;\n        await isar.tWriteTxn(() => objA1.otherLink.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.otherLink.tLoad();\n        expect(newA1.otherLink.value, objB1);\n\n        objA1.otherLink.value = null;\n        await isar.tWriteTxn(() => objA1.otherLink.tSave());\n\n        await newA1.otherLink.tLoad();\n        expect(newA1.otherLink.value, null);\n      });\n\n      isarTest('backlink', () async {\n        await isar.tWriteTxn(() async {\n          await linksA.tPut(objA1);\n          await linksB.tPut(objB1);\n        });\n\n        objA1.otherLink.value = objB1;\n        await isar.tWriteTxn(() => objA1.otherLink.tSave());\n\n        final newB2 = await linksB.tGet(objB1.id!);\n        await newB2!.linkBacklinks.tLoad();\n\n        expect(newB2.linkBacklinks, [objA1]);\n      });\n    });\n\n    group('self links', () {\n      isarTest('save link', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2, objA3]));\n\n        objA1.selfLinks.addAll([objA2, objA3]);\n\n        await isar.tWriteTxn(() => objA1.selfLinks.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLinks.tLoad();\n        expect(newA1.selfLinks, {objA2, objA3});\n      });\n\n      isarTest('save link in unsaved object', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA2, objA3]));\n\n        objA1.selfLinks.addAll([objA2, objA3]);\n\n        await isar.tWriteTxn(() async {\n          await linksA.tPut(objA1);\n          await objA1.selfLinks.tSave();\n        });\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLinks.tLoad();\n        expect(newA1.selfLinks, {objA2, objA3});\n      });\n\n      isarTest('delete source', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2, objA3]));\n\n        objA1.selfLinks.addAll([objA2, objA3]);\n        await isar.tWriteTxn(() => objA1.selfLinks.tSave());\n\n        final newA2 = await linksA.tGet(objA2.id!);\n        final newA3 = await linksA.tGet(objA3.id!);\n        await Future.wait([\n          newA2!.selfLinksBacklink.tLoad(),\n          newA3!.selfLinksBacklink.tLoad(),\n        ]);\n\n        expect(newA2.selfLinksBacklink, [objA1]);\n        expect(newA3.selfLinksBacklink, [objA1]);\n\n        await isar.tWriteTxn(() => linksA.tDelete(objA1.id!));\n\n        final newestA2 = await linksA.tGet(objA2.id!);\n        final newestA3 = await linksA.tGet(objA3.id!);\n        await Future.wait([\n          newestA2!.selfLinksBacklink.tLoad(),\n          newestA3!.selfLinksBacklink.tLoad(),\n        ]);\n\n        expect(newestA2.selfLinksBacklink, const <LinkModelA>[]);\n        expect(newestA3.selfLinksBacklink, const <LinkModelA>[]);\n      });\n\n      isarTest('delete target', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2, objA3]));\n\n        objA1.selfLinks.addAll([objA2, objA3]);\n        await isar.tWriteTxn(() => objA1.selfLinks.tSave());\n        await isar.tWriteTxn(() => linksA.tDelete(objA2.id!));\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLinks.tLoad();\n        expect(newA1.selfLinks, [objA3]);\n      });\n\n      isarTest('delete with same obj links', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2, objA3]));\n\n        objA1.selfLinks.addAll([objA1, objA2, objA3]);\n        await isar.tWriteTxn(() => objA1.selfLinks.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        final newA2 = await linksA.tGet(objA2.id!);\n        final newA3 = await linksA.tGet(objA3.id!);\n\n        await Future.wait([\n          newA1!.selfLinksBacklink.tLoad(),\n          newA2!.selfLinksBacklink.tLoad(),\n          newA3!.selfLinksBacklink.tLoad(),\n        ]);\n\n        expect(newA1.selfLinksBacklink, [objA1]);\n        expect(newA2.selfLinksBacklink, [objA1]);\n        expect(newA3.selfLinksBacklink, [objA1]);\n\n        await isar.tWriteTxn(() => linksA.tDelete(objA1.id!));\n\n        final newestA1 = await linksA.tGet(objA1.id!);\n        expect(newestA1, null);\n\n        final newestA2 = await linksA.tGet(objA2.id!);\n        final newestA3 = await linksA.tGet(objA3.id!);\n\n        await Future.wait([\n          newestA2!.selfLinksBacklink.tLoad(),\n          newestA3!.selfLinksBacklink.tLoad(),\n        ]);\n\n        expect(newestA2.selfLinksBacklink, const <LinkModelA>[]);\n        expect(newestA3.selfLinksBacklink, const <LinkModelA>[]);\n      });\n\n      isarTest('duplicate links', () async {\n        await isar.tWriteTxn(() => linksA.tPutAll([objA1, objA2, objA3]));\n\n        objA1.selfLinks.addAll([objA2, objA2, objA2, objA3]);\n        await isar.tWriteTxn(() => objA1.selfLinks.tSave());\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        await newA1!.selfLinks.tLoad();\n        expect(newA1.selfLinks, {objA2, objA3});\n\n        final newA2 = await linksA.tGet(objA2.id!);\n        await newA2!.selfLinksBacklink.tLoad();\n        expect(newA2.selfLinksBacklink, [objA1]);\n      });\n    });\n\n    group('multiple links', () {\n      isarTest('.load() / .save()', () async {\n        await isar.tWriteTxn(() async {\n          await linksA.tPutAll([objA1, objA2, objA3]);\n          await linksB.tPutAll([objB1, objB2, objB3]);\n        });\n\n        objA1.selfLink.value = objA2;\n        objA1.selfLinks.addAll([objA2, objA3]);\n        objA1.otherLink.value = objB1;\n        objA1.otherLinks.addAll([objB1, objB2, objB3]);\n\n        objA2.selfLink.value = objA3;\n        objA2.selfLinks.addAll([objA3, objA1]);\n        objA2.otherLink.value = objB2;\n        objA2.otherLinks.addAll([objB1, objB2, objB3]);\n\n        objA3.selfLink.value = objA1;\n        objA3.selfLinks.addAll([objA1, objA2]);\n        objA3.otherLink.value = objB3;\n        objA3.otherLinks.addAll([objB1, objB2, objB3]);\n\n        await isar.tWriteTxn(() async {\n          await Future.wait([\n            objA1.selfLink.tSave(),\n            objA1.selfLinks.tSave(),\n            objA1.otherLink.tSave(),\n            objA1.otherLinks.tSave(),\n            objA2.selfLink.tSave(),\n            objA2.selfLinks.tSave(),\n            objA2.otherLink.tSave(),\n            objA2.otherLinks.tSave(),\n            objA3.selfLink.tSave(),\n            objA3.selfLinks.tSave(),\n            objA3.otherLink.tSave(),\n            objA3.otherLinks.tSave(),\n          ]);\n        });\n\n        final newA1 = await linksA.tGet(objA1.id!);\n        final newA2 = await linksA.tGet(objA2.id!);\n        final newA3 = await linksA.tGet(objA3.id!);\n        final newB1 = await linksB.tGet(objB1.id!);\n        final newB2 = await linksB.tGet(objB2.id!);\n        final newB3 = await linksB.tGet(objB3.id!);\n\n        await Future.wait([\n          newA1!.selfLink.tLoad(),\n          newA1.selfLinks.tLoad(),\n          newA1.otherLink.tLoad(),\n          newA1.otherLinks.tLoad(),\n          newA2!.selfLink.tLoad(),\n          newA2.selfLinks.tLoad(),\n          newA2.otherLink.tLoad(),\n          newA2.otherLinks.tLoad(),\n          newA3!.selfLink.tLoad(),\n          newA3.selfLinks.tLoad(),\n          newA3.otherLink.tLoad(),\n          newA3.otherLinks.tLoad(),\n          newB1!.linkBacklinks.tLoad(),\n          newB1.linksBacklinks.tLoad(),\n          newB2!.linkBacklinks.tLoad(),\n          newB2.linksBacklinks.tLoad(),\n          newB3!.linkBacklinks.tLoad(),\n          newB3.linksBacklinks.tLoad(),\n        ]);\n\n        expect(newA1.selfLink.value, objA2);\n        expect(newA1.selfLinks, {objA2, objA3});\n        expect(newA1.otherLink.value, objB1);\n        expect(newA1.otherLinks, {objB1, objB2, objB3});\n\n        expect(newA2.selfLink.value, objA3);\n        expect(newA2.selfLinks, {objA3, objA1});\n        expect(newA2.otherLink.value, objB2);\n        expect(newA2.otherLinks, {objB1, objB2, objB3});\n\n        expect(newA3.selfLink.value, objA1);\n        expect(newA3.selfLinks, {objA1, objA2});\n        expect(newA3.otherLink.value, objB3);\n        expect(newA3.otherLinks, {objB1, objB2, objB3});\n\n        expect(newB1.linkBacklinks, [objA1]);\n        expect(newB1.linksBacklinks, {objA1, objA2, objA3});\n\n        expect(newB2.linkBacklinks, [objA2]);\n        expect(newB2.linksBacklinks, {objA1, objA2, objA3});\n\n        expect(newB3.linkBacklinks, [objA3]);\n        expect(newB3.linksBacklinks, {objA1, objA2, objA3});\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/links/backlink_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'backlink_test.g.dart';\n\n@collection\nclass LinkModelA {\n  LinkModelA(this.name);\n\n  Id? id;\n\n  final String name;\n\n  final links = IsarLinks<LinkModelB>();\n\n  final selfLinks = IsarLinks<LinkModelA>();\n\n  @Backlink(to: 'selfLinks')\n  final selfBacklinks = IsarLinks<LinkModelA>();\n\n  @override\n  String toString() {\n    return 'LinkModelA($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelA && id == other.id && other.name == name;\n  }\n}\n\n@collection\nclass LinkModelB {\n  LinkModelB(this.name);\n\n  Id? id;\n\n  final String name;\n\n  @Backlink(to: 'links')\n  final backlinks = IsarLinks<LinkModelA>();\n\n  @override\n  String toString() {\n    return 'LinkModelB($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelB && id == other.id && other.name == name;\n  }\n}\n\nvoid main() {\n  group('Link', () {\n    late Isar isar;\n    late LinkModelA a1;\n    late LinkModelA a2;\n    late LinkModelA a3;\n    late LinkModelB b1;\n    late LinkModelB b2;\n    late LinkModelB b3;\n\n    setUp(() async {\n      isar = await openTempIsar([LinkModelASchema, LinkModelBSchema]);\n\n      a1 = LinkModelA('modelA1');\n      a2 = LinkModelA('modelA2');\n      a3 = LinkModelA('modelA3');\n      b1 = LinkModelB('modelB1');\n      b2 = LinkModelB('modelB2');\n      b3 = LinkModelB('modelB3');\n\n      await isar.writeTxn(() async {\n        await isar.linkModelAs.putAll([a1, a2, a3]);\n        await isar.linkModelBs.putAll([b1, b2, b3]);\n      });\n    });\n\n    test('otherlinks', () async {\n      await isar.tWriteTxn(() async {\n        a1.links.addAll([b1, b2, b3]);\n        await a1.links.tSave();\n\n        a2.links.addAll([b1, b2]);\n        await a2.links.tSave();\n      });\n\n      await b1.backlinks.tLoad();\n      expect(b1.backlinks, {a1, a2});\n      await b2.backlinks.tLoad();\n      expect(b2.backlinks, {a1, a2});\n      await b3.backlinks.tLoad();\n      expect(b3.backlinks, {a1});\n\n      await isar.tWriteTxn(() => isar.linkModelBs.tDelete(b2.id!));\n\n      await a1.links.tLoad();\n      expect(a1.links, {b1, b3});\n      await a2.links.tLoad();\n      expect(a2.links, {b1});\n\n      await isar.tWriteTxn(() => isar.linkModelAs.tDelete(a1.id!));\n\n      await b1.backlinks.tLoad();\n      expect(b1.backlinks, {a2});\n      await b2.backlinks.tLoad();\n      expect(b2.backlinks, <LinkModelA>{});\n      await b3.backlinks.tLoad();\n      expect(b3.backlinks, <LinkModelA>{});\n\n      await a1.links.tLoad();\n      expect(a1.links, <LinkModelB>{});\n    });\n\n    test('selflinks', () async {\n      a1.selfLinks.addAll([a1, a2, a3]);\n      a2.selfLinks.addAll([a2, a3]);\n      await isar.tWriteTxn(() async {\n        await a1.selfLinks.tSave();\n        await a2.selfLinks.tSave();\n      });\n\n      await a1.selfBacklinks.tLoad();\n      expect(a1.selfBacklinks, {a1});\n      await a2.selfBacklinks.tLoad();\n      expect(a2.selfBacklinks, {a1, a2});\n      await a3.selfBacklinks.tLoad();\n      expect(a3.selfBacklinks, {a1, a2});\n\n      await isar.tWriteTxn(() => isar.linkModelAs.tDelete(a2.id!));\n\n      await a1.selfBacklinks.tLoad();\n      expect(a1.selfBacklinks, {a1});\n      await a2.selfBacklinks.tLoad();\n      expect(a2.selfBacklinks, <LinkModelA>{});\n      await a3.selfBacklinks.tLoad();\n      expect(a3.selfBacklinks, {a1});\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/links/link_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'link_test.g.dart';\n\n@collection\nclass LinkModelA {\n  LinkModelA(this.name);\n\n  Id? id;\n\n  final String name;\n\n  final link = IsarLink<LinkModelB>();\n\n  @override\n  String toString() {\n    return 'LinkModelA($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelA && id == other.id && other.name == name;\n  }\n}\n\n@collection\nclass LinkModelB {\n  LinkModelB(this.name);\n\n  Id? id;\n\n  final String name;\n\n  @override\n  String toString() {\n    return 'LinkModelB($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelB && id == other.id && other.name == name;\n  }\n}\n\nvoid main() {\n  group('Link', () {\n    late Isar isar;\n    late LinkModelA a1;\n    late LinkModelA a2;\n    late LinkModelB b1;\n    late LinkModelB b2;\n\n    setUp(() async {\n      isar = await openTempIsar([LinkModelASchema, LinkModelBSchema]);\n\n      a1 = LinkModelA('modelA1');\n      a2 = LinkModelA('modelA2');\n      b1 = LinkModelB('modelB1');\n      b2 = LinkModelB('modelB2');\n    });\n\n    isarTest('.isAttached .isLoaded .isChanged', () async {\n      void verify(bool attached, bool loaded, bool changed) {\n        expect(a1.link.isAttached, attached);\n        expect(a1.link.isLoaded, loaded);\n        expect(a1.link.isChanged, changed);\n      }\n\n      verify(false, false, false);\n\n      a1.link.value = b1;\n      verify(false, true, true);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1, saveLinks: false);\n        await isar.linkModelBs.tPut(b1);\n      });\n      verify(true, true, true);\n\n      a1.link.value = b1;\n      await isar.tWriteTxn(() => a1.link.tSave());\n      verify(true, true, false);\n    });\n\n    isarTest('.save() / .load() manually', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPut(b1);\n      });\n\n      a1.link.value = b1;\n      await isar.tWriteTxn(a1.link.tSave);\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.link.tLoad();\n      expect(newA1.link.value, b1);\n\n      newA1.link.value = null;\n      await isar.tWriteTxn(newA1.link.tSave);\n\n      await a1.link.tLoad();\n      expect(a1.link.value, isNull);\n    });\n\n    isarTestSync('save automatic', () {\n      isar.writeTxnSync(() => isar.linkModelBs.putSync(b1));\n\n      a1.link.value = b1;\n      isar.writeTxnSync(() => isar.linkModelAs.putSync(a1));\n\n      final newA1 = isar.linkModelAs.getSync(a1.id!);\n      newA1!.link.loadSync();\n      expect(newA1.link.value, b1);\n\n      newA1.link.value = null;\n      isar.writeTxnSync(() => isar.linkModelAs.putSync(newA1));\n\n      a1.link.loadSync();\n      expect(a1.link.value, isNull);\n    });\n\n    isarTestSync('create target', () {\n      a1.link.value = b1;\n      isar.writeTxnSync(() => isar.linkModelAs.putSync(a1));\n\n      final newA1 = isar.linkModelAs.getSync(a1.id!);\n      newA1!.link.loadSync();\n      expect(newA1.link.value, b1);\n    });\n\n    isarTestSync('load automatic', () {\n      a1.link.value = b1;\n      a2.link.value = b2;\n      isar.writeTxnSync(() => isar.linkModelAs.putAllSync([a1, a2]));\n\n      final newA1 = isar.linkModelAs.getSync(a1.id!);\n      expect(newA1!.link.value, b1);\n\n      final newA2 = isar.linkModelAs.getSync(a2.id!);\n      expect(newA2!.link.value, b2);\n    });\n\n    isarTest('reset', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPutAll([a1, a2]);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.link.value = b1;\n      a2.link.value = b2;\n      await isar.tWriteTxn(() async {\n        await a1.link.tSave();\n        await a2.link.tSave();\n      });\n\n      await isar.tWriteTxn(() => a2.link.tReset());\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.link.tLoad();\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.link.tLoad();\n\n      expect(a1.link.value, b1);\n      expect(newA1.link.value, b1);\n      expect(a2.link.value, null);\n      expect(newA2.link.value, null);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/links/links_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'links_test.g.dart';\n\n@collection\nclass LinkModelA {\n  LinkModelA(this.name);\n\n  Id? id;\n\n  final String name;\n\n  final links = IsarLinks<LinkModelB>();\n\n  @override\n  String toString() {\n    return 'LinkModelA($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelA && id == other.id && other.name == name;\n  }\n}\n\n@collection\nclass LinkModelB {\n  LinkModelB(this.name);\n\n  Id? id;\n\n  final String name;\n\n  @override\n  String toString() {\n    return 'LinkModelB($id, $name)';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is LinkModelB && id == other.id && other.name == name;\n  }\n}\n\nvoid main() {\n  group('Link', () {\n    late Isar isar;\n    late LinkModelA a1;\n    late LinkModelA a2;\n    late LinkModelB b1;\n    late LinkModelB b2;\n\n    setUp(() async {\n      isar = await openTempIsar([LinkModelASchema, LinkModelBSchema]);\n\n      a1 = LinkModelA('modelA1');\n      a2 = LinkModelA('modelA2');\n      b1 = LinkModelB('modelB1');\n      b2 = LinkModelB('modelB2');\n    });\n\n    isarTest('.tSave() / .load() manually', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.links.addAll([b1, b2]);\n      await isar.tWriteTxn(a1.links.tSave);\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b1, b2});\n\n      newA1.links.remove(b1);\n      await isar.tWriteTxn(newA1.links.tSave);\n\n      await a1.links.tLoad();\n      expect(a1.links, {b2});\n    });\n\n    isarTest('.load() preserves changes', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.links.add(b2);\n      await isar.tWriteTxn(a1.links.tSave);\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      expect(newA1!.links.add(b1), true);\n      expect(newA1.links.remove(b2), true);\n      await newA1.links.tLoad();\n      expect(newA1.links, {b1});\n\n      expect(newA1.links.remove(b1), true);\n      expect(newA1.links.add(b2), true);\n      await newA1.links.tLoad();\n      expect(a1.links, {b2});\n    });\n\n    isarTest('.load(overrideChanges: true)', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.links.add(b2);\n      await isar.tWriteTxn(a1.links.tSave);\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      expect(newA1!.links.add(b1), true);\n      expect(newA1.links.remove(b2), true);\n      await newA1.links.tLoad(overrideChanges: true);\n      expect(newA1.links, {b2});\n    });\n\n    isarTestSync('save automatic', () {\n      isar.writeTxnSync(() => isar.linkModelBs.putAllSync([b1, b2]));\n\n      a1.links.addAll([b1, b2]);\n      expect(a1.links.isChanged, true);\n      isar.writeTxnSync(() => isar.linkModelAs.putSync(a1));\n      expect(a1.links.isChanged, false);\n\n      final newA1 = isar.linkModelAs.getSync(a1.id!);\n      newA1!.links.loadSync();\n      expect(newA1.links, {b1, b2});\n\n      newA1.links.remove(b1);\n      expect(newA1.links.isChanged, true);\n      isar.writeTxnSync(() => isar.linkModelAs.putSync(newA1));\n      expect(newA1.links.isChanged, false);\n\n      a1.links.loadSync();\n      expect(a1.links, {b2});\n    });\n\n    isarTestSync('create target', () {\n      a1.links.addAll({b1, b2});\n      isar.writeTxnSync(() => isar.linkModelAs.putSync(a1));\n\n      final newA1 = isar.linkModelAs.getSync(a1.id!);\n      newA1!.links.loadSync();\n      expect(newA1.links, {b1, b2});\n    });\n\n    isarTestSync('load automatic', () {\n      a1.links.addAll([b1, b2]);\n      a2.links.add(b2);\n      isar.writeTxnSync(() => isar.linkModelAs.putAllSync([a1, a2]));\n\n      final newA1 = isar.linkModelAs.getSync(a1.id!);\n      newA1!.links.remove(b1);\n      expect(newA1.links, {b2});\n\n      final newA2 = isar.linkModelAs.getSync(a2.id!);\n      newA2!.links.add(b1);\n      expect(newA2.links, {b1, b2});\n    });\n\n    isarTest('.add() new target', () async {\n      await isar.tWriteTxn(() => isar.linkModelAs.tPut(a1));\n\n      expect(a1.links.add(b1), true);\n      expect(a1.links.add(b1), false);\n      expect(a2.links.add(b2), true);\n      expect(a2.links.add(b2), false);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelBs.tPutAll([b1, b2]);\n        await isar.linkModelAs.tPut(a2, saveLinks: false);\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b1});\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n      expect(newA2.links, {b2});\n    });\n\n    isarTest('.add() existing target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      expect(a1.links.add(b1), true);\n      final newB1 = await isar.linkModelBs.tGet(b1.id!);\n      expect(a1.links.add(newB1!), false);\n\n      expect(a2.links.add(b2), true);\n      final newB2 = await isar.linkModelBs.tGet(b2.id!);\n      expect(a2.links.add(newB2!), true);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a2, saveLinks: false);\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b1});\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n      expect(newA2.links, {b2});\n    });\n\n    isarTest('.add() after .remove()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n      });\n\n      expect(a1.links.remove(b1), true);\n      expect(a2.links.remove(b2), true);\n\n      expect(a1.links.add(b1), true);\n      expect(a2.links.add(b2), true);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelBs.tPutAll([b1, b2]);\n        await isar.linkModelAs.tPut(a2, saveLinks: false);\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b1});\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n      expect(newA2.links, {b2});\n    });\n\n    isarTest('.remove() new target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n      });\n\n      expect(a1.links.remove(b1), true);\n      expect(a1.links.remove(b1), false);\n      expect(a2.links.remove(b2), true);\n      expect(a2.links.remove(b2), false);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a2);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n\n        final newA1 = await isar.linkModelAs.tGet(a1.id!);\n        newA1!.links.addAll([b1, b2]);\n        await newA1.links.tSave();\n\n        final newA2 = await isar.linkModelAs.tGet(a2.id!);\n        newA2!.links.addAll([b1, b2]);\n        await newA2.links.tSave();\n\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b2});\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n      expect(newA2.links, {b1});\n    });\n\n    isarTest('.remove() existing target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n\n        a1.links.addAll([b1, b2]);\n        await a1.links.tSave();\n      });\n\n      expect(a1.links.remove(b1), true);\n      final newB1 = await isar.linkModelBs.tGet(b1.id!);\n      expect(a1.links.remove(newB1), false);\n\n      expect(a2.links.remove(b2), true);\n      final newB2 = await isar.linkModelBs.tGet(b2.id!);\n      expect(a2.links.remove(newB2), true);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a2);\n\n        final newA2 = await isar.linkModelAs.tGet(a2.id!);\n        newA2!.links.addAll([b1, b2]);\n        await newA2.links.tSave();\n\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b2});\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n      expect(newA2.links, {b1});\n    });\n\n    isarTest('.remove() without .add()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n\n        a1.links.addAll([b1, b2]);\n        await a1.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      final newB1 = await isar.linkModelBs.tGet(b1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links.remove(newB1), true);\n\n      await isar.tWriteTxn(() async {\n        await a1.links.tSave();\n      });\n\n      await a1.links.tLoad();\n      expect(newA1.links, {b2});\n    });\n\n    isarTest('.remove() after .add()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n      });\n\n      a1.links.addAll([b1, b2]);\n      a2.links.addAll([b1, b2]);\n      expect(a1.links.remove(b1), true);\n      expect(a1.links.remove(b1), false);\n      expect(a2.links.remove(b2), true);\n      expect(a2.links.remove(b2), false);\n\n      await isar.tWriteTxn(() async {\n        await isar.linkModelBs.tPutAll([b1, b2]);\n        await isar.linkModelAs.tPut(a2, saveLinks: false);\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n      expect(newA1.links, {b2});\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n      expect(newA2.links, {b1});\n    });\n\n    isarTest('.contains() / .lookup() new target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n      });\n\n      a1.links.add(b1);\n      a2.links.add(b2);\n\n      expect(a1.links.contains(b1), false);\n      expect(a1.links.lookup(b1), null);\n      expect(a1.links.contains(b2), false);\n      expect(a1.links.lookup(b2), null);\n    });\n\n    isarTest('.contains() / .lookup() existing target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.links.add(b1);\n      a2.links.add(b2);\n\n      final newB1 = await isar.linkModelBs.tGet(b1.id!);\n      expect(a1.links.contains(newB1), true);\n      expect(identical(a1.links.lookup(newB1), b1), true);\n\n      expect(a1.links.contains(b2), false);\n      expect(a1.links.lookup(b2), null);\n    });\n\n    isarTestSync('.contains() / .lookup() loads automatically', () {\n      isar.writeTxnSync(() {\n        a1.links.add(b2);\n        isar.linkModelAs.putSync(a1);\n      });\n\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        expect(newA1!.links.contains(b1), false);\n      }\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        expect(newA1!.links.lookup(b1), null);\n      }\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        expect(newA1!.links.contains(b2), true);\n      }\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        expect(newA1!.links.lookup(b2), b2);\n      }\n    });\n\n    isarTest('.filter()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n\n        a1.links.addAll([b1, b2]);\n        await a1.links.tSave();\n      });\n\n      await qEqualSet(a1.links.filter(), [b1, b2]);\n      await qEqual(a1.links.filter().sortByNameDesc(), [b2, b1]);\n      await qEqualSet(a1.links.filter().idEqualTo(b2.id), [b2]);\n      await qEqualSet(a1.links.filter().idEqualTo(5), []);\n    });\n\n    isarTest('.reset()', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPutAll([a1, a2]);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.links.addAll([b1, b2]);\n      a2.links.add(b2);\n      await isar.tWriteTxn(() async {\n        await a1.links.tSave();\n        await a2.links.tSave();\n      });\n\n      await isar.tWriteTxn(a1.links.tReset);\n\n      final newA1 = await isar.linkModelAs.tGet(a1.id!);\n      await newA1!.links.tLoad();\n\n      final newA2 = await isar.linkModelAs.tGet(a2.id!);\n      await newA2!.links.tLoad();\n\n      expect(a1.links, isEmpty);\n      expect(newA1.links, isEmpty);\n      expect(a2.links, {b2});\n      expect(newA2.links, {b2});\n    });\n\n    isarTest('.toSet() .contains() .lookup() throw if not attached', () async {\n      a2.links.addAll([b1, b2]);\n\n      expect(() => a2.links.toSet(), throwsIsarError('managed by Isar'));\n      expect(() => a2.links.contains(null), throwsIsarError('managed by Isar'));\n      expect(() => a2.links.lookup(null), throwsIsarError('managed by Isar'));\n    });\n\n    isarTest('.iterator .length .toSet() new target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n      });\n\n      a1.links.addAll([b1, b2]);\n      a2.links.addAll([b1, b2]);\n\n      expect(a1.links.iterator.moveNext(), false);\n      expect(a1.links.length, 0);\n      expect(a1.links.toSet(), <LinkModelB>{});\n\n      expect(a2.links.iterator.moveNext(), false);\n      expect(a2.links.length, 0);\n    });\n\n    isarTest('.iterator .length .toSet() existing target', () async {\n      await isar.tWriteTxn(() async {\n        await isar.linkModelAs.tPut(a1);\n        await isar.linkModelBs.tPutAll([b1, b2]);\n      });\n\n      a1.links.addAll([b1, b2]);\n      a2.links.addAll([b1, b2]);\n\n      expect(a1.links.iterator.moveNext(), true);\n      expect(a1.links.length, 2);\n      expect(a1.links.toSet(), {b1, b2});\n\n      expect(a2.links.iterator.moveNext(), false);\n      expect(a2.links.length, 0);\n    });\n\n    isarTestSync('.iterator .length .toSet() load automatically', () async {\n      isar.writeTxnSync(() {\n        a1.links.addAll([b1, b2]);\n        isar.linkModelAs.putSync(a1);\n      });\n\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        final results = <LinkModelB>[];\n        final iterator = newA1!.links.iterator;\n        while (iterator.moveNext()) {\n          results.add(iterator.current);\n        }\n        expect(results, {b1, b2});\n      }\n\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        expect(newA1!.links.length, 2);\n      }\n\n      {\n        final newA1 = isar.linkModelAs.getSync(a1.id!);\n        expect(newA1!.links.toSet(), {b1, b2});\n      }\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/max_size_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'max_size_test.g.dart';\n\n@collection\nclass Model {\n  final Id id = Isar.autoIncrement;\n\n  final value = '123456789' * 1000;\n}\n\nvoid main() {\n  group('Max Size', () {\n    test('default', () async {\n      final isar = await openTempIsar([ModelSchema]);\n      await isar.writeTxn(() async {\n        // TODO: figure out why 10000 doesn't work on armv7 Android\n        await isar.models.putAll(List.filled(1000, Model()));\n      });\n    });\n\n    test('10MB', () async {\n      final isar = await openTempIsar([ModelSchema], maxSizeMiB: 10);\n\n      expect(\n        isar.writeTxn(() async {\n          await isar.models.putAll(List.filled(1000, Model()));\n        }),\n        throwsIsarError('The database is full'),\n      );\n\n      await isar.writeTxn(() async {\n        await isar.models.putAll(List.filled(50, Model()));\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/add_remove_collection_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'add_remove_collection_test.g.dart';\n\n@collection\nclass Model1 {\n  Model1(this.id, this.value);\n\n  Id? id;\n\n  @Index()\n  String? value;\n\n  final link = IsarLink<Model1>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Model1 && id == other.id && value == other.value;\n}\n\n@collection\nclass Model2 {\n  Model2(this.id, this.value);\n\n  Id? id;\n\n  @Index()\n  String? value;\n\n  final link = IsarLink<Model1>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Model2 && id == other.id && value == other.value;\n}\n\nvoid main() {\n  isarTest('Add collection', () async {\n    final isar1 = await openTempIsar([Model1Schema]);\n    final obj1A = Model1(5, 'col1_a');\n    final obj1B = Model1(15, 'col1_b');\n    await isar1.tWriteTxn(() {\n      return isar1.model1s.tPutAll([obj1A, obj1B]);\n    });\n    await isar1.model1s.verify([obj1A, obj1B]);\n    expect(await isar1.close(), true);\n\n    final isar2 =\n        await openTempIsar([Model1Schema, Model2Schema], name: isar1.name);\n    await isar2.model1s.verify([obj1A, obj1B]);\n    await isar2.model2s.verify([]);\n    final obj2 = Model2(null, 'col2_a');\n    await isar2.tWriteTxn(() {\n      return isar2.model2s.tPut(obj2);\n    });\n    await isar2.model2s.verify([obj2]);\n  });\n\n  isarTest('Remove collection', () async {\n    final isar1 = await openTempIsar([Model1Schema, Model2Schema]);\n    final obj1A = Model1(5, 'col1_a');\n    final obj1B = Model1(15, 'col1_b');\n    final obj2A = Model2(15, 'col2_a');\n    final obj2B = Model2(15, 'col2_a');\n    await isar1.tWriteTxn(() async {\n      await isar1.model1s.tPutAll([obj1A, obj1B]);\n      await isar1.model2s.tPutAll([obj2A, obj2B]);\n\n      obj1A.link.value = obj1B;\n      await obj1A.link.tSave();\n\n      obj2A.link.value = obj1A;\n      await obj2A.link.tSave();\n    });\n    await isar1.model1s.verify([obj1A, obj1B]);\n    await isar1.model1s.verifyLink('link', [obj1A.id!], [obj1B.id!]);\n    await isar1.model2s.verify([obj2A, obj2B]);\n    await isar1.model2s.verifyLink('link', [obj2A.id!], [obj1A.id!]);\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Model1Schema], name: isar1.name);\n    await isar2.model1s.verify([obj1A, obj1B]);\n    await isar2.model1s.verifyLink('link', [obj1A.id!], [obj1B.id!]);\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar(\n      [Model1Schema, Model2Schema],\n      name: isar1.name,\n    );\n    await isar3.model1s.verify([obj1A, obj1B]);\n    await isar3.model1s.verifyLink('link', [obj1A.id!], [obj1B.id!]);\n    await isar3.model2s.verify([]);\n    await isar3.model2s.verifyLink('link', [], []);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/add_remove_embedded_field_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'add_remove_embedded_field_test.g.dart';\n\n@collection\n@Name('Col')\nclass Col1 {\n  Col1(this.id, this.value);\n  Id? id;\n\n  Embedded1? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col1 && id == other.id && value == other.value;\n\n  @override\n  String toString() {\n    // TODO: implement toString\n    return 'Col1{id: $id, value: $value}';\n  }\n}\n\n@embedded\n@Name('Embedded')\nclass Embedded1 {\n  Embedded1([this.value]);\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Embedded1 && value == other.value;\n\n  @override\n  String toString() {\n    // TODO: implement toString\n    return 'Embedded1{value: $value}';\n  }\n}\n\n@collection\n@Name('Col')\nclass Col2 {\n  Col2(this.id, this.value);\n  Id? id;\n\n  Embedded2? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col2 && id == other.id && value == other.value;\n\n  @override\n  String toString() {\n    // TODO: implement toString\n    return 'Col2{id: $id, value: $value}';\n  }\n}\n\n@embedded\n@Name('Embedded')\nclass Embedded2 {\n  Embedded2([this.newValue, this.value]);\n\n  int? newValue;\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Embedded2 && value == other.value && newValue == other.newValue;\n\n  @override\n  String toString() {\n    // TODO: implement toString\n    return 'Embedded2{newValue: $newValue, value: $value}';\n  }\n}\n\nvoid main() {\n  isarTest('Add field', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() {\n      return isar1.col1s.tPutAll([\n        Col1(1, Embedded1('value1')),\n        Col1(2, Embedded1('value2')),\n      ]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    await qEqual(isar2.col2s.where(), [\n      Col2(1, Embedded2(null, 'value1')),\n      Col2(2, Embedded2(null, 'value2')),\n    ]);\n    await isar2.tWriteTxn(() {\n      return isar2.col2s.tPutAll([\n        Col2(1, Embedded2(1, 'value4')),\n        Col2(3, Embedded2(3, 'value5')),\n      ]);\n    });\n    await qEqual(isar2.col2s.where(), [\n      Col2(1, Embedded2(1, 'value4')),\n      Col2(2, Embedded2(null, 'value2')),\n      Col2(3, Embedded2(3, 'value5')),\n    ]);\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Col1Schema], name: isar1.name);\n    await qEqual(isar3.col1s.where(), [\n      Col1(1, Embedded1('value4')),\n      Col1(2, Embedded1('value2')),\n      Col1(3, Embedded1('value5')),\n    ]);\n    expect(await isar3.close(), true);\n  });\n\n  /*isarTest('Remove field', () async {\n    final isar1 = await openTempIsar([Col2Schema]);\n    await isar1.writeTxn(() {\n      return isar1.col2s.putAll([\n        Col2(1, 'value1', ['hi']),\n        Col2(2, 'value2', ['val2', 'val22']),\n      ]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col1Schema], name: isar1.name);\n    await qEqual(isar2.col1s.where(), [\n      Col1(1, 'value1'),\n      Col1(2, 'value2'),\n    ]);\n    await isar2.writeTxn(() {\n      return isar2.col1s.put(Col1(1, 'value3'));\n    });\n    await qEqual(isar2.col1s.where(), [\n      Col1(1, 'value3'),\n      Col1(2, 'value2'),\n    ]);\n    expect(await isar2.close(), true);\n  });*/\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/add_remove_field_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'add_remove_field_test.g.dart';\n\n@collection\n@Name('Col')\nclass Col1 {\n  Col1(this.id, this.value);\n  Id? id;\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col1 && id == other.id && value == other.value;\n}\n\n@collection\n@Name('Col')\nclass Col2 {\n  Col2(this.id, this.value, this.newValues);\n  Id? id;\n\n  String? value;\n\n  List<String>? newValues;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col2 &&\n      id == other.id &&\n      value == other.value &&\n      listEquals(newValues, other.newValues);\n}\n\nvoid main() {\n  isarTest('Add field', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() {\n      return isar1.col1s.tPutAll([Col1(1, 'value1'), Col1(2, 'value2')]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    await qEqual(isar2.col2s.where(), [\n      Col2(1, 'value1', null),\n      Col2(2, 'value2', null),\n    ]);\n    await isar2.tWriteTxn(() {\n      return isar2.col2s.tPutAll([\n        Col2(1, 'value3', ['hi']),\n        Col2(3, 'value4', [])\n      ]);\n    });\n    await qEqual(isar2.col2s.where(), [\n      Col2(1, 'value3', ['hi']),\n      Col2(2, 'value2', null),\n      Col2(3, 'value4', []),\n    ]);\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Col1Schema], name: isar1.name);\n    await qEqual(isar3.col1s.where(), [\n      Col1(1, 'value3'),\n      Col1(2, 'value2'),\n      Col1(3, 'value4'),\n    ]);\n  });\n\n  isarTest('Remove field', () async {\n    final isar1 = await openTempIsar([Col2Schema]);\n    await isar1.writeTxn(() {\n      return isar1.col2s.putAll([\n        Col2(1, 'value1', ['hi']),\n        Col2(2, 'value2', ['val2', 'val22']),\n      ]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col1Schema], name: isar1.name);\n    await qEqual(isar2.col1s.where(), [\n      Col1(1, 'value1'),\n      Col1(2, 'value2'),\n    ]);\n    await isar2.writeTxn(() {\n      return isar2.col1s.put(Col1(1, 'value3'));\n    });\n    await qEqual(isar2.col1s.where(), [\n      Col1(1, 'value3'),\n      Col1(2, 'value2'),\n    ]);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/add_remove_index_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'add_remove_index_test.g.dart';\n\n@collection\n@Name('Col')\nclass Col1 {\n  Col1(this.id, this.value);\n  Id? id;\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col1 && id == other.id && value == other.value;\n}\n\n@collection\n@Name('Col')\nclass Col2 {\n  Col2(this.id, this.value);\n  Id? id;\n\n  @Index(unique: true)\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col2 && id == other.id && value == other.value;\n}\n\nvoid main() {\n  isarTest('Add remove index', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() {\n      return isar1.col1s.tPutAll([Col1(1, 'a'), Col1(2, 'b')]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    await qEqual(isar2.col2s.where(), [Col2(1, 'a'), Col2(2, 'b')]);\n    expect(await isar2.col2s.getByValue('a'), Col2(1, 'a'));\n    await isar2.tWriteTxn(() {\n      return isar2.col2s.tPutAll([Col2(1, 'c'), Col2(3, 'd')]);\n    });\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Col1Schema], name: isar1.name);\n    await qEqual(isar3.col1s.where(), [\n      Col1(1, 'c'),\n      Col1(2, 'b'),\n      Col1(3, 'd'),\n    ]);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/add_remove_link_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'add_remove_link_test.g.dart';\n\n@collection\n@Name('Col')\nclass Col1 {\n  Col1(this.id);\n  Id? id;\n\n  final IsarLink<Col1> link = IsarLink<Col1>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Col1 && id == other.id;\n}\n\n@collection\n@Name('Col')\nclass Col2 {\n  Col2(this.id);\n  Id? id;\n\n  @Name('link')\n  final IsarLink<Col2> link1 = IsarLink<Col2>();\n\n  final IsarLink<Col2> link2 = IsarLink<Col2>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Col2 && id == other.id;\n}\n\nvoid main() {\n  isarTest('Add remove link', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() async {\n      final obj = Col1(1);\n      final linkedObj = Col1(2);\n      await isar1.col1s.tPutAll([obj, linkedObj]);\n\n      obj.link.value = linkedObj;\n      await obj.link.tSave();\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    final obj = await isar2.col2s.tGet(1);\n    await obj!.link1.tLoad();\n    await obj.link2.tLoad();\n    expect(obj.link1.value, Col2(2));\n    expect(obj.link2.value, null);\n    await isar2.tWriteTxn(() async {\n      final obj3 = Col2(3);\n      await isar2.col2s.tPut(obj3);\n\n      obj.link2.value = obj3;\n      await obj.link2.tSave();\n    });\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Col1Schema], name: isar1.name);\n    final obj1 = await isar3.col1s.tGet(1);\n    await obj1!.link.tLoad();\n    expect(obj1.link.value, Col1(2));\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/change_field_embedded_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'change_field_embedded_test.g.dart';\n\n@collection\n@Name('Col')\nclass Model1 {\n  Model1(this.id, this.value);\n\n  Id? id;\n\n  Embedded1? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Model1 && other.id == id && other.value == value;\n}\n\n@collection\n@Name('Col')\nclass Model2 {\n  Model2(this.id, this.value);\n\n  Id? id;\n\n  Embedded2? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Model2 && other.id == id && other.value == value;\n}\n\n@embedded\nclass Embedded1 {\n  Embedded1([this.value]);\n\n  String? value;\n}\n\n@embedded\nclass Embedded2 {\n  Embedded2([this.value]);\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Embedded2 && other.value == value;\n}\n\nvoid main() {\n  isarTest('Change field embedded', () async {\n    final isar1 = await openTempIsar([Model1Schema]);\n    await isar1.tWriteTxn(() {\n      return isar1.model1s.tPutAll([\n        Model1(1, Embedded1('a')),\n        Model1(2, Embedded1('b')),\n      ]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Model2Schema], name: isar1.name);\n    await qEqual(isar2.model2s.where(), [\n      Model2(1, null),\n      Model2(2, null),\n    ]);\n    await isar2.tWriteTxn(() {\n      return isar2.model2s.tPut(Model2(1, Embedded2('abc')));\n    });\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Model1Schema], name: isar1.name);\n    await qEqual(isar3.model1s.where(), [\n      Model1(1, null),\n      Model1(2, null),\n    ]);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/change_field_nullability_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'change_field_nullability_test.g.dart';\n\n@collection\n@Name('Col')\nclass Col1 {\n  Col1(this.id, this.value);\n  Id? id;\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col1 && id == other.id && value == other.value;\n}\n\n@collection\n@Name('Col')\nclass Col2 {\n  Col2(this.id, this.value);\n  Id? id;\n\n  late String value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Col2 && id == other.id && value == other.value;\n}\n\nvoid main() {\n  isarTest('Change field nullability', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() {\n      return isar1.col1s.tPutAll([Col1(1, 'a'), Col1(2, null)]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    await qEqual(isar2.col2s.where(), [Col2(1, 'a'), Col2(2, '')]);\n    await isar2.tWriteTxn(() {\n      return isar2.col2s.tPut(Col2(1, 'c'));\n    });\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Col1Schema], name: isar1.name);\n    await qEqual(isar3.col1s.where(), [Col1(1, 'c'), Col1(2, null)]);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/change_field_type_test.dart",
    "content": "@TestOn('vm')\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'change_field_type_test.g.dart';\n\n@collection\n@Name('Col')\nclass Model1 {\n  Model1(this.id, this.value, this.str);\n\n  Id? id;\n\n  @Index()\n  @Index(composite: [CompositeIndex('str')])\n  String? value;\n\n  @Index(composite: [CompositeIndex('value')])\n  String str;\n}\n\n@collection\n@Name('Col')\nclass Model2 {\n  Model2(this.id, this.value, this.str);\n\n  Id? id;\n\n  @Index()\n  @Index(composite: [CompositeIndex('str')])\n  int? value;\n\n  @Index(composite: [CompositeIndex('value')])\n  String str;\n}\n\nvoid main() {\n  isarTest('Change field type', () async {\n    final isar1 = await openTempIsar([Model1Schema]);\n    final obj1A = Model1(1, 'a', 'OBJ1');\n    final obj1B = Model1(2, 'bbb', 'OBJ2');\n    await isar1.tWriteTxn(() {\n      return isar1.model1s.tPutAll([obj1A, obj1B]);\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Model2Schema], name: isar1.name);\n    final obj2A = Model2(1, null, 'OBJ1');\n    final obj2B = Model2(2, null, 'OBJ2');\n    await isar2.model2s.verify([obj2A, obj2B]);\n    final obj2C = Model2(1, 123, 'OBJ3');\n    await isar2.tWriteTxn(() {\n      return isar2.model2s.tPut(obj2C);\n    });\n    await isar2.model2s.verify([obj2C, obj2B]);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/migration/change_link_links_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'change_link_links_test.g.dart';\n\n@collection\n@Name('Col')\nclass Col1 {\n  Col1(this.id);\n  Id? id;\n\n  final IsarLink<Col1> link = IsarLink<Col1>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Col1 && id == other.id;\n}\n\n@collection\n@Name('Col')\nclass Col2 {\n  Col2(this.id);\n  Id? id;\n\n  final IsarLinks<Col2> link = IsarLinks<Col2>();\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Col2 && id == other.id;\n}\n\nvoid main() {\n  isarTest('Add remove link', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() async {\n      final linkedObj = Col1(2);\n      final obj = Col1(1);\n      await isar1.col1s.tPutAll([obj, linkedObj]);\n\n      obj.link.value = linkedObj;\n      await obj.link.tSave();\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    final obj = await isar2.col2s.tGet(1);\n    await obj!.link.tLoad();\n    expect(obj.link, {Col2(2)});\n    await isar2.tWriteTxn(() async {\n      await obj.link.tReset();\n\n      final obj3 = Col2(3);\n      await isar2.col2s.tPut(obj3);\n\n      obj.link.add(obj3);\n      await obj.link.tSave();\n    });\n    expect(await isar2.close(), true);\n\n    final isar3 = await openTempIsar([Col1Schema], name: isar1.name);\n    final obj1 = await isar3.col1s.tGet(1);\n    await obj1!.link.tLoad();\n    expect(obj1.link.value, Col1(3));\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/mutli_type_model.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\n\npart 'mutli_type_model.g.dart';\n\n@collection\nclass MultiTypeModel {\n  Id? id;\n\n  bool? boolValueN;\n\n  bool boolValue = false;\n\n  short? intValueN;\n\n  short intValue = 0;\n\n  float? floatValueN;\n\n  float floatValue = 0;\n\n  int? longValueN;\n\n  int longValue = 0;\n\n  double? doubleValueN;\n\n  double doubleValue = 0;\n\n  DateTime? dateTimeValueN;\n\n  DateTime dateTimeValue = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);\n\n  String? stringValueN;\n\n  String stringValue = '';\n\n  List<bool> boolList = [];\n\n  List<bool?> boolNList = [];\n\n  List<bool>? boolListN;\n\n  List<bool?>? boolNListN;\n\n  List<byte>? byteListN;\n\n  List<byte> byteList = Uint8List(0);\n\n  List<short> intList = [];\n\n  List<short?> intNList = [];\n\n  List<short>? intListN;\n\n  List<short?>? intNListN;\n\n  List<float> floatList = [];\n\n  List<float?> floatNList = [];\n\n  List<float>? floatListN;\n\n  List<float?>? floatNListN;\n\n  List<int> longList = [];\n\n  List<int?> longNList = [];\n\n  List<int>? longListN;\n\n  List<int?>? longNListN;\n\n  List<double> doubleList = [];\n\n  List<double?> doubleNList = [];\n\n  List<double>? doubleListN;\n\n  List<double?>? doubleNListN;\n\n  List<DateTime> dateTimeList = [];\n\n  List<DateTime?> dateTimeNList = [];\n\n  List<DateTime>? dateTimeListN;\n\n  List<DateTime?>? dateTimeNListN;\n\n  List<String> stringList = [];\n\n  List<String?> stringNList = [];\n\n  List<String>? stringListN;\n\n  List<String?>? stringNListN;\n}\n"
  },
  {
    "path": "packages/isar_test/test/name_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'name_test.g.dart';\n\n@collection\n@Name('NameModelN')\nclass NameModel {\n  @Name('idN')\n  Id? id;\n\n  @Index()\n  @Name('valueN')\n  String? value;\n\n  @Index(composite: [CompositeIndex('value')])\n  @Name('otherValueN')\n  String? otherValue;\n\n  @Name('linkN')\n  IsarLinks<NameModel> link = IsarLinks<NameModel>();\n\n  @Backlink(to: 'link')\n  @Name('backlink')\n  IsarLinks<NameModel> backlink = IsarLinks<NameModel>();\n}\n\nvoid main() {\n  group('Name', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([NameModelSchema]);\n    });\n\n    isarTest('json', () async {\n      await isar.tWriteTxn(\n        () => isar.nameModels.tPut(\n          NameModel()\n            ..value = 'test'\n            ..otherValue = 'test2',\n        ),\n      );\n\n      expect(await isar.nameModels.where().exportJson(), [\n        {\n          'idN': 1,\n          'valueN': 'test',\n          'otherValueN': 'test2',\n        },\n      ]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/open_close_isar_listener_test.dart",
    "content": "import 'dart:async';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport 'user_model.dart';\n\nvoid main() {\n  group('open / close isar listener', () {\n    isarTest('Open listener', () async {\n      final streamController = StreamController<Isar>();\n      void openListener(Isar isar) => streamController.add(isar);\n      Isar.addOpenListener(openListener);\n      final listener = Listener(streamController.stream);\n\n      final isar1 = await openTempIsar([UserModelSchema]);\n      final listenedIsar1 = await listener.next;\n      expect(isar1, listenedIsar1);\n      await isar1.close(deleteFromDisk: true);\n\n      final isar2 = await openTempIsar([UserModelSchema]);\n      final listenerIsar2 = await listener.next;\n      expect(isar2, listenerIsar2);\n      await isar2.close(deleteFromDisk: true);\n\n      Isar.removeOpenListener(openListener);\n      await listener.done();\n      await streamController.close();\n    });\n\n    isarTest('Close listener', () async {\n      final streamController = StreamController<String>();\n      void closeListener(String name) => streamController.add(name);\n      Isar.addCloseListener(closeListener);\n      final listener = Listener(streamController.stream);\n\n      final isar1 = await openTempIsar([UserModelSchema]);\n      await isar1.close(deleteFromDisk: true);\n      final listenedName1 = await listener.next;\n      expect(isar1.name, listenedName1);\n\n      final isar2 = await openTempIsar([UserModelSchema]);\n      await isar2.close(deleteFromDisk: true);\n      final listenedName2 = await listener.next;\n      expect(isar2.name, listenedName2);\n\n      Isar.removeCloseListener(closeListener);\n      await listener.done();\n      await streamController.close();\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/other_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('Other', () {\n    isarTestVm('split words', () {\n      expect(Isar.splitWords(''), <String>[]);\n      expect(Isar.splitWords('single'), ['single']);\n      expect(\n          Isar.splitWords(\n            'The quick (“brown”) fox can’t jump 32.3 feet, right?',\n          ),\n          [\n            'The',\n            'quick',\n            'brown',\n            'fox',\n            'can’t',\n            'jump',\n            '32.3',\n            'feet',\n            'right'\n          ]);\n      expect(\n        Isar.splitWords('איך בלש תפס גמד רוצח עז קטנה?'),\n        ['איך', 'בלש', 'תפס', 'גמד', 'רוצח', 'עז', 'קטנה'],\n      );\n      expect(\n          Isar.splitWords(\n            'В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!',\n          ),\n          [\n            'В',\n            'чащах',\n            'юга',\n            'жил',\n            'бы',\n            'цитрус',\n            'Да',\n            'но',\n            'фальшивый',\n            'экземпляр'\n          ]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/aggregation_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport '../mutli_type_model.dart';\n\nvoid main() {\n  group('Aggregation', () {\n    late Isar isar;\n    late IsarCollection<MultiTypeModel> col;\n\n    setUp(() async {\n      isar = await openTempIsar([MultiTypeModelSchema]);\n      col = isar.multiTypeModels;\n    });\n\n    group('int', () {\n      setUp(() async {\n        await isar.writeTxn(\n          () => col.putAll([\n            MultiTypeModel()..intValue = -5,\n            MultiTypeModel()..intValue = 0,\n            MultiTypeModel()\n              ..intValue = 10\n              ..intValueN = 10,\n          ]),\n        );\n      });\n\n      isarTest('min', () async {\n        expect(await col.where().intValueProperty().tMin(), -5);\n        expect(await col.where().intValueNProperty().tMin(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .intValueEqualTo(10)\n              .intValueProperty()\n              .tMin(),\n          10,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .intValueProperty()\n              .tMin(),\n          null,\n        );\n      });\n\n      isarTest('max', () async {\n        expect(await col.where().intValueProperty().tMax(), 10);\n        expect(await col.where().intValueNProperty().tMax(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .intValueEqualTo(-5)\n              .intValueProperty()\n              .tMax(),\n          -5,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .intValueProperty()\n              .tMax(),\n          null,\n        );\n      });\n\n      isarTest('sum', () async {\n        expect(await col.where().intValueProperty().tSum(), 5);\n        expect(await col.where().intValueNProperty().tSum(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .intValueEqualTo(10)\n              .intValueProperty()\n              .tSum(),\n          10,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .intValueProperty()\n              .tSum(),\n          0,\n        );\n      });\n\n      isarTest('average', () async {\n        expect(await col.where().intValueProperty().tAverage(), 5 / 3);\n        expect(await col.where().intValueNProperty().tAverage(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .intValueEqualTo(10)\n              .intValueProperty()\n              .tAverage(),\n          10.0,\n        );\n\n        expect(\n          (await col\n                  .where()\n                  .filter()\n                  .boolValueEqualTo(true)\n                  .intValueProperty()\n                  .tAverage())\n              .isNaN,\n          true,\n        );\n      });\n\n      isarTest('count', () async {\n        expect(await col.where().intValueProperty().tCount(), 3);\n        expect(await col.where().intValueNProperty().tCount(), 3);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .intValueEqualTo(10)\n              .intValueProperty()\n              .tCount(),\n          1,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .intValueProperty()\n              .tCount(),\n          0,\n        );\n      });\n    });\n\n    group('float', () {\n      setUp(() async {\n        await isar.writeTxn(\n          () => col.putAll([\n            MultiTypeModel()..floatValue = -5.0,\n            MultiTypeModel()..floatValue = 0.0,\n            MultiTypeModel()\n              ..floatValue = 10.0\n              ..floatValueN = 10.0,\n          ]),\n        );\n      });\n\n      isarTest('min', () async {\n        expect(await col.where().floatValueProperty().tMin(), -5.0);\n        expect(await col.where().floatValueNProperty().tMin(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .floatValueGreaterThan(9)\n              .floatValueProperty()\n              .tMin(),\n          10.0,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .floatValueProperty()\n              .tMin(),\n          null,\n        );\n      });\n\n      isarTest('max', () async {\n        expect(await col.where().floatValueProperty().tMax(), 10.0);\n        expect(await col.where().floatValueNProperty().tMax(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .floatValueLessThan(-4)\n              .floatValueProperty()\n              .tMax(),\n          -5.0,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .floatValueProperty()\n              .tMax(),\n          null,\n        );\n      });\n\n      isarTest('sum', () async {\n        expect(await col.where().floatValueProperty().tSum(), 5.0);\n        expect(await col.where().floatValueNProperty().tSum(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .floatValueGreaterThan(9)\n              .floatValueProperty()\n              .tSum(),\n          10.0,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .floatValueProperty()\n              .tSum(),\n          0,\n        );\n      });\n\n      isarTest('average', () async {\n        expect(await col.where().floatValueProperty().tAverage(), 5 / 3);\n        expect(await col.where().floatValueNProperty().tAverage(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .floatValueGreaterThan(9)\n              .floatValueProperty()\n              .tAverage(),\n          10.0,\n        );\n\n        expect(\n          (await col\n                  .where()\n                  .filter()\n                  .boolValueEqualTo(true)\n                  .floatValueProperty()\n                  .tAverage())\n              .isNaN,\n          true,\n        );\n      });\n\n      isarTest('count', () async {\n        expect(await col.where().floatValueProperty().tCount(), 3);\n        expect(await col.where().floatValueNProperty().tCount(), 3);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .floatValueGreaterThan(9)\n              .floatValueProperty()\n              .tCount(),\n          1,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .floatValueProperty()\n              .tCount(),\n          0,\n        );\n      });\n    });\n\n    group('long', () {\n      setUp(() async {\n        await isar.writeTxn(\n          () => col.putAll([\n            MultiTypeModel()..longValue = -5,\n            MultiTypeModel()..longValue = 0,\n            MultiTypeModel()\n              ..longValue = 10\n              ..longValueN = 10,\n          ]),\n        );\n      });\n\n      isarTest('min', () async {\n        expect(await col.where().longValueProperty().tMin(), -5);\n        expect(await col.where().longValueNProperty().tMin(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .longValueEqualTo(10)\n              .longValueProperty()\n              .tMin(),\n          10,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .longValueProperty()\n              .tMin(),\n          null,\n        );\n      });\n\n      isarTest('max', () async {\n        expect(await col.where().longValueProperty().tMax(), 10);\n        expect(await col.where().longValueNProperty().tMax(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .longValueEqualTo(-5)\n              .longValueProperty()\n              .tMax(),\n          -5,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .longValueProperty()\n              .tMax(),\n          null,\n        );\n      });\n\n      isarTest('sum', () async {\n        expect(await col.where().longValueProperty().tSum(), 5);\n        expect(await col.where().longValueNProperty().tSum(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .longValueEqualTo(10)\n              .longValueProperty()\n              .tSum(),\n          10,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .longValueProperty()\n              .tSum(),\n          0,\n        );\n      });\n\n      isarTest('average', () async {\n        expect(await col.where().longValueProperty().tAverage(), 5 / 3);\n        expect(await col.where().longValueNProperty().tAverage(), 10);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .longValueEqualTo(10)\n              .longValueProperty()\n              .tAverage(),\n          10.0,\n        );\n\n        expect(\n          (await col\n                  .where()\n                  .filter()\n                  .boolValueEqualTo(true)\n                  .longValueProperty()\n                  .tAverage())\n              .isNaN,\n          true,\n        );\n      });\n\n      isarTest('count', () async {\n        expect(await col.where().longValueProperty().tCount(), 3);\n        expect(await col.where().longValueNProperty().tCount(), 3);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .longValueEqualTo(10)\n              .longValueProperty()\n              .tCount(),\n          1,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .longValueProperty()\n              .tCount(),\n          0,\n        );\n      });\n    });\n\n    group('double', () {\n      setUp(() async {\n        await isar.writeTxn(\n          () => col.putAll([\n            MultiTypeModel()..doubleValue = -5.0,\n            MultiTypeModel()..doubleValue = 0.0,\n            MultiTypeModel()\n              ..doubleValue = 10.0\n              ..doubleValueN = 10.0,\n          ]),\n        );\n      });\n\n      isarTest('min', () async {\n        expect(await col.where().doubleValueProperty().tMin(), -5.0);\n        expect(await col.where().doubleValueNProperty().tMin(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .doubleValueGreaterThan(9)\n              .doubleValueProperty()\n              .tMin(),\n          10.0,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .doubleValueProperty()\n              .tMin(),\n          null,\n        );\n      });\n\n      isarTest('max', () async {\n        expect(await col.where().doubleValueProperty().tMax(), 10.0);\n        expect(await col.where().doubleValueNProperty().tMax(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .doubleValueLessThan(-4)\n              .doubleValueProperty()\n              .tMax(),\n          -5.0,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .doubleValueProperty()\n              .tMax(),\n          null,\n        );\n      });\n\n      isarTest('sum', () async {\n        expect(await col.where().doubleValueProperty().tSum(), 5.0);\n        expect(await col.where().doubleValueNProperty().tSum(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .doubleValueGreaterThan(9)\n              .doubleValueProperty()\n              .tSum(),\n          10.0,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .doubleValueProperty()\n              .tSum(),\n          0,\n        );\n      });\n\n      isarTest('average', () async {\n        expect(await col.where().doubleValueProperty().tAverage(), 5 / 3);\n        expect(await col.where().doubleValueNProperty().tAverage(), 10.0);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .doubleValueGreaterThan(9)\n              .doubleValueProperty()\n              .tAverage(),\n          10.0,\n        );\n\n        expect(\n          (await col\n                  .where()\n                  .filter()\n                  .boolValueEqualTo(true)\n                  .doubleValueProperty()\n                  .tAverage())\n              .isNaN,\n          true,\n        );\n      });\n\n      isarTest('count', () async {\n        expect(await col.where().doubleValueProperty().tCount(), 3);\n        expect(await col.where().doubleValueNProperty().tCount(), 3);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .doubleValueGreaterThan(9)\n              .doubleValueProperty()\n              .tCount(),\n          1,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .doubleValueProperty()\n              .tCount(),\n          0,\n        );\n      });\n    });\n\n    group('DateTime', () {\n      DateTime date(int milliseconds) =>\n          DateTime.fromMillisecondsSinceEpoch(milliseconds);\n\n      setUp(() async {\n        await isar.writeTxn(\n          () => col.putAll([\n            MultiTypeModel()..dateTimeValue = date(-5),\n            MultiTypeModel()..dateTimeValue = date(0),\n            MultiTypeModel()\n              ..dateTimeValue = date(10)\n              ..dateTimeValueN = date(10),\n          ]),\n        );\n      });\n\n      isarTest('min', () async {\n        expect(await col.where().dateTimeValueProperty().tMin(), date(-5));\n        expect(await col.where().dateTimeValueNProperty().tMin(), date(10));\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .dateTimeValueEqualTo(date(10))\n              .dateTimeValueProperty()\n              .tMin(),\n          date(10),\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .dateTimeValueProperty()\n              .tMin(),\n          null,\n        );\n      });\n\n      isarTest('max', () async {\n        expect(await col.where().dateTimeValueProperty().tMax(), date(10));\n        expect(await col.where().dateTimeValueNProperty().tMax(), date(10));\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .dateTimeValueEqualTo(date(-5))\n              .dateTimeValueProperty()\n              .tMax(),\n          date(-5),\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .dateTimeValueProperty()\n              .tMax(),\n          null,\n        );\n      });\n\n      isarTest('count', () async {\n        expect(await col.where().dateTimeValueProperty().tCount(), 3);\n        expect(await col.where().dateTimeValueNProperty().tCount(), 3);\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .dateTimeValueEqualTo(date(10))\n              .dateTimeValueProperty()\n              .tCount(),\n          1,\n        );\n\n        expect(\n          await col\n              .where()\n              .filter()\n              .boolValueEqualTo(true)\n              .dateTimeValueProperty()\n              .tCount(),\n          0,\n        );\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/embedded_test.dart",
    "content": "void main() {}\n"
  },
  {
    "path": "packages/isar_test/test/query/group_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport '../user_model.dart';\n\nvoid main() {\n  group('Filter Groups', () {\n    late Isar isar;\n    late IsarCollection<UserModel> users;\n\n    setUp(() async {\n      isar = await openTempIsar([UserModelSchema]);\n      users = isar.userModels;\n\n      await isar.writeTxn(() async {\n        await users.putAll([\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Emma', 30, true),\n          UserModel.fill('Tina', 40, false),\n          UserModel.fill('Simon', 30, false),\n          UserModel.fill('Bjorn', 40, true),\n        ]);\n      });\n    });\n\n    isarTest('Simple or', () async {\n      await qEqualSet(\n        users.where().filter().ageEqualTo(20).or().ageEqualTo(30),\n        [\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Emma', 30, true),\n          UserModel.fill('Simon', 30, false),\n        ],\n      );\n    });\n\n    isarTest('Simple and', () async {\n      await qEqualSet(\n        users.where().filter().ageEqualTo(40).and().adminEqualTo(true),\n        [UserModel.fill('Bjorn', 40, true)],\n      );\n\n      await qEqualSet(\n        users.where().filter().ageEqualTo(40).adminEqualTo(true),\n        [UserModel.fill('Bjorn', 40, true)],\n      );\n    });\n\n    isarTest('Simple xor', () async {\n      await qEqualSet(\n        users.where().filter().ageGreaterThan(20).xor().adminEqualTo(false),\n        [\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Emma', 30, true),\n          UserModel.fill('Bjorn', 40, true),\n        ],\n      );\n    });\n\n    isarTest('Or followed by and', () async {\n      await qEqualSet(\n        users\n            .where()\n            .filter()\n            .ageEqualTo(20)\n            .or()\n            .ageEqualTo(30)\n            .and()\n            .nameEqualTo('Emma'),\n        [\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Emma', 30, true),\n        ],\n      );\n    });\n\n    isarTest('And followed by or', () async {\n      await qEqualSet(\n        users\n            .where()\n            .filter()\n            .ageEqualTo(30)\n            .and()\n            .nameEqualTo('Simon')\n            .or()\n            .ageEqualTo(20),\n        [\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Simon', 30, false),\n        ],\n      );\n    });\n\n    isarTest('Or followed by group', () async {\n      await qEqualSet(\n        users\n            .where()\n            .filter()\n            .ageEqualTo(20)\n            .or()\n            .group((q) => q.ageEqualTo(30).and().nameEqualTo('Emma')),\n        [\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Emma', 30, true),\n        ],\n      );\n    });\n\n    isarTest('And followed by group', () async {\n      await qEqualSet(\n        users\n            .where()\n            .filter()\n            .ageEqualTo(30)\n            .and()\n            .group((q) => q.nameEqualTo('Simon').or().ageEqualTo(20)),\n        [UserModel.fill('Simon', 30, false)],\n      );\n    });\n\n    isarTest('Nested groups', () async {\n      await qEqualSet(\n        users.where().filter().group(\n              (QueryBuilder<UserModel, UserModel, QFilterCondition> q) => q\n                  .nameEqualTo('Simon')\n                  .or()\n                  .group((q) => q.ageEqualTo(30).or().ageEqualTo(20)),\n            ),\n        [\n          UserModel.fill('Simon', 30, false),\n          UserModel.fill('David', 20, false),\n          UserModel.fill('Emma', 30, true),\n        ],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/is_empty_is_not_empty_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'is_empty_is_not_empty_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.value);\n\n  Id id = Isar.autoIncrement;\n\n  String? value;\n}\n\nvoid main() {\n  group('Query isEmpty / isNotEmpty', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n\n      await isar.tWriteTxn(\n        () => isar.models.tPutAll(List.generate(100, (i) => Model('model $i'))),\n      );\n    });\n\n    isarTest('.isEmpty()', () async {\n      expect(await isar.models.where().tIsEmpty(), false);\n      expect(await isar.models.where().limit(999999).tIsEmpty(), false);\n      expect(await isar.models.where().limit(1).tIsEmpty(), false);\n      expect(await isar.models.where().limit(0).tIsEmpty(), true);\n\n      expect(\n        await isar.models.filter().valueStartsWith('model').tIsEmpty(),\n        false,\n      );\n      expect(\n        await isar.models.filter().valueEqualTo('model 1').tIsEmpty(),\n        false,\n      );\n      expect(\n        await isar.models.filter().valueStartsWith('non existing').tIsEmpty(),\n        true,\n      );\n      expect(\n        await isar.models\n            .filter()\n            .valueStartsWith('model 1')\n            .and()\n            .valueEqualTo('model 2')\n            .tIsEmpty(),\n        true,\n      );\n      expect(\n        await isar.models\n            .filter()\n            .valueEqualTo('model 1')\n            .or()\n            .valueEqualTo('model 2')\n            .tIsEmpty(),\n        false,\n      );\n\n      await isar.tWriteTxn(() => isar.models.where().limit(99).tDeleteAll());\n\n      expect(await isar.models.where().tIsEmpty(), false);\n      expect(await isar.models.where().limit(999999).tIsEmpty(), false);\n      expect(await isar.models.where().limit(1).tIsEmpty(), false);\n      expect(await isar.models.where().limit(0).tIsEmpty(), true);\n\n      await isar.tWriteTxn(() => isar.models.where().tDeleteAll());\n\n      expect(await isar.models.where().tIsEmpty(), true);\n      expect(await isar.models.where().limit(999999).tIsEmpty(), true);\n      expect(await isar.models.where().limit(1).tIsEmpty(), true);\n      expect(await isar.models.where().limit(0).tIsEmpty(), true);\n\n      await isar.tWriteTxn(() => isar.models.tPut(Model(null)));\n\n      expect(await isar.models.where().tIsEmpty(), false);\n      expect(await isar.models.where().limit(999999).tIsEmpty(), false);\n      expect(await isar.models.where().limit(1).tIsEmpty(), false);\n      expect(await isar.models.where().limit(0).tIsEmpty(), true);\n    });\n\n    isarTest('.isNotEmpty()', () async {\n      expect(await isar.models.where().tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(999999).tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(1).tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(0).tIsNotEmpty(), false);\n\n      expect(\n        await isar.models.filter().valueStartsWith('model').tIsNotEmpty(),\n        true,\n      );\n      expect(\n        await isar.models.filter().valueEqualTo('model 1').tIsNotEmpty(),\n        true,\n      );\n      expect(\n        await isar.models\n            .filter()\n            .valueStartsWith('non existing')\n            .tIsNotEmpty(),\n        false,\n      );\n      expect(\n        await isar.models\n            .filter()\n            .valueStartsWith('model 1')\n            .and()\n            .valueEqualTo('model 2')\n            .tIsNotEmpty(),\n        false,\n      );\n      expect(\n        await isar.models\n            .filter()\n            .valueEqualTo('model 1')\n            .or()\n            .valueEqualTo('model 2')\n            .tIsNotEmpty(),\n        true,\n      );\n\n      await isar.tWriteTxn(() => isar.models.where().limit(99).tDeleteAll());\n\n      expect(await isar.models.where().tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(999999).tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(1).tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(0).tIsNotEmpty(), false);\n\n      await isar.tWriteTxn(() => isar.models.where().tDeleteAll());\n\n      expect(await isar.models.where().tIsNotEmpty(), false);\n      expect(await isar.models.where().limit(999999).tIsNotEmpty(), false);\n      expect(await isar.models.where().limit(1).tIsNotEmpty(), false);\n      expect(await isar.models.where().limit(0).tIsNotEmpty(), false);\n\n      await isar.tWriteTxn(() => isar.models.tPut(Model(null)));\n\n      expect(await isar.models.where().tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(999999).tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(1).tIsNotEmpty(), true);\n      expect(await isar.models.where().limit(0).tIsNotEmpty(), false);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/multi_filter_test.dart",
    "content": "// ignore_for_file: inference_failure_on_function_invocation\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'multi_filter_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.id, this.value);\n  final Id id;\n\n  @Index()\n  final int value;\n\n  @override\n  bool operator ==(Object other) => other is Model && id == other.id;\n\n  @override\n  int get hashCode => id.hashCode;\n}\n\nvoid main() {\n  group('Multi filters', () {\n    late Isar isar;\n\n    late Model model0;\n    late Model model1;\n    late Model model2;\n    late Model model3;\n\n    setUp(() async {\n      model0 = Model(0, 0);\n      model1 = Model(1, 1);\n      model2 = Model(2, 2);\n      model3 = Model(3, 3);\n      isar = await openTempIsar([ModelSchema]);\n      await isar.writeTxn(() {\n        return isar.models.putAll([model0, model1, model2, model3]);\n      });\n    });\n\n    group('where anyOf', () {\n      isarTest('zero elements', () async {\n        final q = isar.models.where().anyOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching element', () async {\n        final q = isar.models.where().anyOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model2]);\n      });\n\n      isarTest('two matching elements', () async {\n        final q = isar.models.where().anyOf(\n          [0, 2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model0, model2]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.where().anyOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n      });\n\n      isarTest('one matching and one non-matching elements', () async {\n        final q = isar.models.where().anyOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model3]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.where().anyOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n      });\n    });\n\n    group('filter anyOf', () {\n      isarTest('zero elements', () async {\n        final q = isar.models.filter().anyOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model0, model1, model2, model3]);\n\n        final notQ = isar.models.filter().not().anyOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching element', () async {\n        final q = isar.models.filter().anyOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model2]);\n\n        final notQ = isar.models.filter().not().anyOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model3]);\n      });\n\n      isarTest('two matching elements', () async {\n        final q = isar.models.filter().anyOf(\n          [0, 2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model0, model2]);\n\n        final notQ = isar.models.filter().not().anyOf(\n          [0, 2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model1, model3]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.filter().anyOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().anyOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching and one non-matching elements', () async {\n        final q = isar.models.filter().anyOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model3]);\n\n        final notQ = isar.models.filter().not().anyOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.filter().anyOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().anyOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n    });\n\n    group('filter allOf', () {\n      isarTest('zero elements', () async {\n        final q = isar.models.filter().allOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model0, model1, model2, model3]);\n\n        final notQ = isar.models.filter().not().allOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching element', () async {\n        final q = isar.models.filter().allOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model2]);\n\n        final notQ = isar.models.filter().not().allOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model3]);\n      });\n\n      isarTest('two matching elements', () async {\n        final q = isar.models.filter().allOf(\n          [2, 2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model2]);\n\n        final notQ = isar.models.filter().not().allOf(\n          [2, 2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model3]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.filter().allOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().allOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching and one non-matching elements', () async {\n        final q = isar.models.filter().allOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().allOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.filter().allOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().allOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n    });\n\n    group('filter oneOf', () {\n      isarTest('zero elements', () async {\n        final q = isar.models.filter().oneOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model0, model1, model2, model3]);\n\n        final notQ = isar.models.filter().not().oneOf(\n          <int>[],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching element', () async {\n        final q = isar.models.filter().oneOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model2]);\n\n        final notQ = isar.models.filter().not().oneOf(\n          [2],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model3]);\n      });\n\n      isarTest('two matching elements', () async {\n        final q = isar.models.filter().oneOf(\n          [2, 2, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model3]);\n\n        final notQ = isar.models.filter().not().oneOf(\n          [2, 2, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.filter().oneOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().oneOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n\n      isarTest('one matching and one non-matching elements', () async {\n        final q = isar.models.filter().oneOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, [model3]);\n\n        final notQ = isar.models.filter().not().oneOf(\n          [7, 3],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2]);\n      });\n\n      isarTest('one non-matching element', () async {\n        final q = isar.models.filter().oneOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(q, []);\n\n        final notQ = isar.models.filter().not().oneOf(\n          [5],\n          (q, int element) => q.valueEqualTo(element),\n        );\n        await qEqual(notQ, [model0, model1, model2, model3]);\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/offset_limit_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'offset_limit_test.g.dart';\n\n@collection\nclass Model {\n  Model(this.value);\n\n  final Id id = Isar.autoIncrement;\n\n  @Index(type: IndexType.value)\n  final String value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Model && other.id == id && other.value == value;\n}\n\nvoid main() {\n  group('Offset Limit', () {\n    late Isar isar;\n    late IsarCollection<Model> col;\n\n    late List<Model> objects;\n    late List<Model> sorted;\n    late Model objA;\n    late Model objA2;\n    late Model objB;\n    late Model objC;\n    late Model objC2;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n      col = isar.models;\n\n      objA = Model('A');\n      objA2 = Model('A');\n      objB = Model('B');\n      objC = Model('C');\n      objC2 = Model('C');\n      objects = [objB, objA, objC, objA2, objC2];\n      sorted = [objA, objA2, objB, objC, objC2];\n\n      await isar.writeTxn(() async {\n        await col.putAll(objects);\n      });\n    });\n\n    isarTest('0 offset', () async {\n      await qEqual(col.where().offset(0), objects);\n      await qEqual(col.where().anyValue().offset(0), sorted);\n      await qEqual(col.where().sortByValue().offset(0), sorted);\n    });\n\n    isarTest('big offset', () async {\n      await qEqual(col.where().offset(99), []);\n      await qEqual(col.where().anyValue().offset(99), []);\n      await qEqual(col.where().sortByValue().offset(99), []);\n    });\n\n    isarTest('offset', () async {\n      await qEqual(col.where().offset(2), objects.sublist(2));\n      await qEqual(col.where().anyValue().offset(2), sorted.sublist(2));\n      await qEqual(col.where().sortByValue().offset(2), sorted.sublist(2));\n    });\n\n    isarTest('0 limit', () async {\n      await qEqual(col.where().limit(0), []);\n      await qEqual(col.where().anyValue().limit(0), []);\n      await qEqual(col.where().sortByValue().limit(0), []);\n    });\n\n    isarTest('big limit', () async {\n      await qEqual(col.where().limit(999999), objects);\n      await qEqual(col.where().anyValue().limit(999999), sorted);\n      await qEqual(col.where().sortByValue().limit(999999), sorted);\n    });\n\n    isarTest('limit', () async {\n      await qEqual(col.where().limit(3), objects.sublist(0, 3));\n      await qEqual(col.where().anyValue().limit(3), sorted.sublist(0, 3));\n      await qEqual(col.where().sortByValue().limit(3), sorted.sublist(0, 3));\n    });\n\n    isarTest('offset and limit', () async {\n      await qEqual(col.where().offset(3).limit(1), objects.sublist(3, 4));\n      await qEqual(\n        col.where().anyValue().offset(3).limit(1),\n        sorted.sublist(3, 4),\n      );\n      await qEqual(\n        col.where().sortByValue().offset(3).limit(1),\n        sorted.sublist(3, 4),\n      );\n    });\n\n    isarTest('offset and big limit', () async {\n      await qEqual(col.where().offset(3).limit(1000), objects.sublist(3));\n      await qEqual(\n        col.where().anyValue().offset(3).limit(1000),\n        sorted.sublist(3),\n      );\n      await qEqual(\n        col.where().sortByValue().offset(3).limit(1000),\n        sorted.sublist(3),\n      );\n    });\n\n    isarTest('big offset and limit', () async {\n      await qEqual(col.where().offset(300).limit(5), []);\n      await qEqual(col.where().anyValue().offset(300).limit(5), []);\n      await qEqual(col.where().sortByValue().offset(300).limit(5), []);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/property_test.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport '../type_models.dart';\n\nvoid main() {\n  group('Query property', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([\n        BoolModelSchema,\n        ByteModelSchema,\n        ShortModelSchema,\n        IntModelSchema,\n        FloatModelSchema,\n        DoubleModelSchema,\n        DateTimeModelSchema,\n        StringModelSchema,\n        EnumModelSchema,\n        ObjectModelSchema,\n      ]);\n    });\n\n    isarTest('id property', () async {\n      await isar.tWriteTxn(\n        () => isar.boolModels.tPutAll([\n          BoolModel(),\n          BoolModel(),\n          BoolModel(),\n        ]),\n      );\n\n      await qEqual(\n        isar.boolModels.where().idProperty(),\n        [1, 2, 3],\n      );\n    });\n\n    isarTest('bool property', () async {\n      await isar.tWriteTxn(\n        () => isar.boolModels.tPutAll([\n          BoolModel()\n            ..value = true\n            ..nValue = false,\n          BoolModel()\n            ..value = false\n            ..nValue = true,\n          BoolModel()..value = true,\n        ]),\n      );\n\n      await qEqual(\n        isar.boolModels.where().valueProperty(),\n        [true, false, true],\n      );\n\n      await qEqual(\n        isar.boolModels.where().nValueProperty(),\n        [false, true, null],\n      );\n    });\n\n    isarTest('byte property', () async {\n      await isar.tWriteTxn(\n        () => isar.byteModels.tPutAll([\n          ByteModel()..value = 5,\n          ByteModel()..value = 123,\n          ByteModel()..value = 0,\n        ]),\n      );\n\n      await qEqual(\n        isar.byteModels.where().valueProperty(),\n        [5, 123, 0],\n      );\n    });\n\n    isarTest('short property', () async {\n      await isar.tWriteTxn(\n        () => isar.shortModels.tPutAll([\n          ShortModel()\n            ..value = 1234\n            ..nValue = 55,\n          ShortModel()..value = 444,\n          ShortModel()\n            ..value = 321321\n            ..nValue = 1,\n        ]),\n      );\n\n      await qEqual(\n        isar.shortModels.where().valueProperty(),\n        [1234, 444, 321321],\n      );\n\n      await qEqual(\n        isar.shortModels.where().nValueProperty(),\n        [55, null, 1],\n      );\n    });\n\n    isarTest('int property', () async {\n      await isar.tWriteTxn(\n        () => isar.intModels.tPutAll([\n          IntModel()\n            ..value = -5\n            ..nValue = -99999,\n          IntModel()\n            ..value = Isar.autoIncrement\n            ..nValue = 0,\n          IntModel()..value = 9999,\n        ]),\n      );\n\n      await qEqual(\n        isar.intModels.where().valueProperty(),\n        [-5, Isar.autoIncrement, 9999],\n      );\n\n      await qEqual(\n        isar.intModels.where().nValueProperty(),\n        [-99999, 0, null],\n      );\n    });\n\n    isarTest('float property', () async {\n      await isar.tWriteTxn(\n        () => isar.floatModels.tPutAll([\n          FloatModel()\n            ..value = -5.5\n            ..nValue = double.infinity,\n          FloatModel()..value = 70.7,\n          FloatModel()\n            ..value = double.nan\n            ..nValue = double.negativeInfinity,\n        ]),\n      );\n\n      await qEqual(\n        isar.floatModels.where().valueProperty(),\n        [-5.5, 70.7, double.nan],\n      );\n\n      await qEqual(\n        isar.floatModels.where().nValueProperty(),\n        [double.infinity, null, double.negativeInfinity],\n      );\n    });\n\n    isarTest('double property', () async {\n      await isar.tWriteTxn(\n        () => isar.doubleModels.tPutAll([\n          DoubleModel()\n            ..value = -5.5\n            ..nValue = double.infinity,\n          DoubleModel()..value = 70.7,\n          DoubleModel()\n            ..value = double.nan\n            ..nValue = double.negativeInfinity,\n        ]),\n      );\n\n      await qEqual(\n        isar.doubleModels.where().valueProperty(),\n        [-5.5, 70.7, double.nan],\n      );\n\n      await qEqual(\n        isar.doubleModels.where().nValueProperty(),\n        [double.infinity, null, double.negativeInfinity],\n      );\n    });\n\n    isarTest('DateTime property', () async {\n      await isar.tWriteTxn(\n        () => isar.dateTimeModels.tPutAll([\n          DateTimeModel()..value = DateTime(2022),\n          DateTimeModel()\n            ..value = DateTime(2020)\n            ..nValue = DateTime(2010),\n          DateTimeModel()..value = DateTime(1999),\n        ]),\n      );\n\n      await qEqual(\n        isar.dateTimeModels.where().valueProperty(),\n        [DateTime(2022), DateTime(2020), DateTime(1999)],\n      );\n\n      await qEqual(\n        isar.dateTimeModels.where().nValueProperty(),\n        [null, DateTime(2010), null],\n      );\n    });\n\n    isarTest('String property', () async {\n      await isar.tWriteTxn(\n        () => isar.stringModels.tPutAll([\n          StringModel()\n            ..value = 'Just'\n            ..nValue = 'A',\n          StringModel()..value = 'a',\n          StringModel()\n            ..value = 'test'\n            ..nValue = 'Z',\n        ]),\n      );\n\n      await qEqual(\n        isar.stringModels.where().valueProperty(),\n        ['Just', 'a', 'test'],\n      );\n\n      await qEqual(\n        isar.stringModels.where().nValueProperty(),\n        ['A', null, 'Z'],\n      );\n    });\n\n    isarTest('Object property', () async {\n      await isar.tWriteTxn(\n        () => isar.objectModels.tPutAll([\n          ObjectModel()\n            ..value = EmbeddedModel('E1')\n            ..nValue = EmbeddedModel('XXX'),\n          ObjectModel()\n            ..value = EmbeddedModel('E2')\n            ..nValue = EmbeddedModel('YYY'),\n          ObjectModel()..value = EmbeddedModel('E3'),\n        ]),\n      );\n\n      await qEqual(\n        isar.objectModels.where().valueProperty(),\n        [EmbeddedModel('E1'), EmbeddedModel('E2'), EmbeddedModel('E3')],\n      );\n\n      await qEqual(\n        isar.objectModels.where().nValueProperty(),\n        [EmbeddedModel('XXX'), EmbeddedModel('YYY'), null],\n      );\n    });\n\n    isarTest('Enum property', () async {\n      await isar.tWriteTxn(\n        () => isar.enumModels.tPutAll([\n          EnumModel()..value = TestEnum.option2,\n          EnumModel()\n            ..value = TestEnum.option3\n            ..nValue = TestEnum.option3,\n          EnumModel()..value = TestEnum.option2,\n        ]),\n      );\n\n      await qEqual(\n        isar.enumModels.where().valueProperty(),\n        [TestEnum.option2, TestEnum.option3, TestEnum.option2],\n      );\n\n      await qEqual(\n        isar.enumModels.where().nValueProperty(),\n        [null, TestEnum.option3, null],\n      );\n    });\n\n    isarTest('bool list property', () async {\n      await isar.tWriteTxn(\n        () => isar.boolModels.tPutAll([\n          BoolModel()\n            ..list = [true, false, true]\n            ..nList = [false],\n          BoolModel()..list = [],\n          BoolModel()\n            ..list = [true]\n            ..nList = [],\n        ]),\n      );\n\n      await qEqual(isar.boolModels.where().listProperty(), [\n        [true, false, true],\n        <bool>[],\n        [true]\n      ]);\n\n      await qEqual(isar.boolModels.where().nListProperty(), [\n        [false],\n        null,\n        <bool>[],\n      ]);\n    });\n\n    isarTest('byte list property', () async {\n      await isar.tWriteTxn(\n        () => isar.byteModels.tPutAll([\n          ByteModel()..list = Uint8List.fromList([0, 10, 255]),\n          ByteModel()\n            ..list = []\n            ..nList = [1, 2, 3, 4, 5],\n          ByteModel()..list = [3],\n        ]),\n      );\n\n      await qEqual(\n        isar.byteModels.where().listProperty(),\n        [\n          Uint8List.fromList([0, 10, 255]),\n          Uint8List.fromList([]),\n          Uint8List.fromList([3])\n        ],\n      );\n\n      await qEqual(\n        isar.byteModels.where().nListProperty(),\n        [\n          null,\n          Uint8List.fromList([1, 2, 3, 4, 5]),\n          null\n        ],\n      );\n    });\n\n    isarTest('short list property', () async {\n      await isar.tWriteTxn(\n        () => isar.shortModels.tPutAll([\n          ShortModel()\n            ..list = [-5, 70, 999]\n            ..nList = [],\n          ShortModel()\n            ..list = []\n            ..nList = [1, 2, 3],\n          ShortModel()..list = [0],\n        ]),\n      );\n\n      await qEqual(isar.shortModels.where().listProperty(), [\n        [-5, 70, 999],\n        <int>[],\n        [0],\n      ]);\n\n      await qEqual(isar.shortModels.where().nListProperty(), [\n        <int>[],\n        [1, 2, 3],\n        null,\n      ]);\n    });\n\n    isarTest('int list property', () async {\n      await isar.tWriteTxn(\n        () => isar.intModels.tPutAll([\n          IntModel()\n            ..list = [-5, 70, 999]\n            ..nList = [],\n          IntModel()\n            ..list = []\n            ..nList = [1, 2, 3],\n          IntModel()..list = [0],\n        ]),\n      );\n\n      await qEqual(isar.intModels.where().listProperty(), [\n        [-5, 70, 999],\n        <int>[],\n        [0],\n      ]);\n\n      await qEqual(isar.intModels.where().nListProperty(), [\n        <int>[],\n        [1, 2, 3],\n        null,\n      ]);\n    });\n\n    isarTest('float list property', () async {\n      await isar.tWriteTxn(\n        () => isar.floatModels.tPutAll([\n          FloatModel()\n            ..list = [-5.5, 70.7, 999.999]\n            ..nList = [double.infinity],\n          FloatModel()..list = [],\n          FloatModel()\n            ..list = [0.0]\n            ..nList = [double.maxFinite],\n        ]),\n      );\n\n      await qEqual(isar.floatModels.where().listProperty(), [\n        [-5.5, 70.7, 999.999],\n        <double>[],\n        [0.0]\n      ]);\n\n      await qEqual(isar.floatModels.where().nListProperty(), [\n        [double.infinity],\n        null,\n        [double.maxFinite]\n      ]);\n    });\n\n    isarTest('double list property', () async {\n      await isar.tWriteTxn(\n        () => isar.doubleModels.tPutAll([\n          DoubleModel()\n            ..list = [-5.5, 70.7, 999.999]\n            ..nList = [double.infinity],\n          DoubleModel()..list = [],\n          DoubleModel()\n            ..list = [0.0]\n            ..nList = [double.maxFinite],\n        ]),\n      );\n\n      await qEqual(isar.doubleModels.where().listProperty(), [\n        [-5.5, 70.7, 999.999],\n        <double>[],\n        [0.0]\n      ]);\n\n      await qEqual(isar.doubleModels.where().nListProperty(), [\n        [double.infinity],\n        null,\n        [double.maxFinite]\n      ]);\n    });\n\n    isarTest('DateTime list property', () async {\n      await isar.tWriteTxn(\n        () => isar.dateTimeModels.tPutAll([\n          DateTimeModel()\n            ..list = [DateTime(2019), DateTime(2020)]\n            ..nList = [DateTime(2000), DateTime(2001)],\n          DateTimeModel()\n            ..list = [DateTime(2020)]\n            ..nList = [DateTime(2000)],\n          DateTimeModel()..list = [],\n        ]),\n      );\n\n      await qEqual(isar.dateTimeModels.where().listProperty(), [\n        [DateTime(2019), DateTime(2020)],\n        [DateTime(2020)],\n        <DateTime>[]\n      ]);\n\n      await qEqual(isar.dateTimeModels.where().nListProperty(), [\n        [DateTime(2000), DateTime(2001)],\n        [DateTime(2000)],\n        null,\n      ]);\n    });\n\n    isarTest('String list property', () async {\n      await isar.tWriteTxn(\n        () => isar.stringModels.tPutAll([\n          StringModel()..list = ['Just', 'a', 'test'],\n          StringModel()..list = [],\n          StringModel()\n            ..list = ['']\n            ..nList = ['HELLO'],\n        ]),\n      );\n\n      await qEqual(\n        isar.stringModels.where().listProperty(),\n        [\n          ['Just', 'a', 'test'],\n          <String>[],\n          ['']\n        ],\n      );\n\n      await qEqual(\n        isar.stringModels.where().nListProperty(),\n        [\n          null,\n          null,\n          ['HELLO']\n        ],\n      );\n    });\n\n    isarTest('Object list property', () async {\n      await isar.tWriteTxn(\n        () => isar.objectModels.tPutAll([\n          ObjectModel()\n            ..list = []\n            ..nList = [EmbeddedModel('abc'), EmbeddedModel('def')],\n          ObjectModel()..list = [EmbeddedModel('abc'), EmbeddedModel('def')],\n          ObjectModel()\n            ..list = [EmbeddedModel()]\n            ..nList = [EmbeddedModel()],\n        ]),\n      );\n\n      await qEqual(\n        isar.objectModels.where().listProperty(),\n        [\n          <EmbeddedModel>[],\n          [EmbeddedModel('abc'), EmbeddedModel('def')],\n          [EmbeddedModel()]\n        ],\n      );\n\n      await qEqual(\n        isar.objectModels.where().nListProperty(),\n        [\n          [EmbeddedModel('abc'), EmbeddedModel('def')],\n          null,\n          [EmbeddedModel()]\n        ],\n      );\n    });\n\n    isarTest('Enum list property', () async {\n      await isar.tWriteTxn(\n        () => isar.enumModels.tPutAll([\n          EnumModel()\n            ..list = [TestEnum.option2]\n            ..nList = [TestEnum.option2, TestEnum.option3],\n          EnumModel()..list = [TestEnum.option1],\n          EnumModel()..list = [],\n        ]),\n      );\n\n      await qEqual(\n        isar.enumModels.where().listProperty(),\n        [\n          [TestEnum.option2],\n          [TestEnum.option1],\n          <TestEnum>[]\n        ],\n      );\n\n      await qEqual(\n        isar.enumModels.where().nListProperty(),\n        [\n          [TestEnum.option2, TestEnum.option3],\n          null,\n          null\n        ],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/sort_by_distinct_by_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nimport '../user_model.dart';\n\nvoid main() {\n  group('Sort By', () {\n    late Isar isar;\n    late IsarCollection<UserModel> users;\n\n    setUp(() async {\n      isar = await openTempIsar([UserModelSchema]);\n      users = isar.userModels;\n\n      await isar.writeTxn(\n        () => users.putAll([\n          UserModel.fill('a', 100, true),\n          UserModel.fill('a', 200, true),\n          UserModel.fill('c', 1, false),\n          UserModel.fill('c', 2, true),\n          UserModel.fill('b', 10, true),\n          UserModel.fill('b', 20, false),\n        ]),\n      );\n    });\n\n    isarTest('.sortBy()', () async {\n      await qEqual(\n        users.where().sortByName().nameProperty(),\n        ['a', 'a', 'b', 'b', 'c', 'c'],\n      );\n\n      await qEqual(\n        users.where().sortByName().thenByNameDesc().nameProperty(),\n        ['a', 'a', 'b', 'b', 'c', 'c'],\n      );\n\n      await qEqual(\n        users.where().sortByAge().ageProperty(),\n        [1, 2, 10, 20, 100, 200],\n      );\n\n      await qEqual(\n        users.where().sortByAdmin().adminProperty(),\n        [false, false, true, true, true, true],\n      );\n    });\n\n    isarTest('.sortByDesc()', () async {\n      await qEqual(\n        users.where().sortByNameDesc().nameProperty(),\n        ['c', 'c', 'b', 'b', 'a', 'a'],\n      );\n\n      await qEqual(\n        users.where().sortByAgeDesc().ageProperty(),\n        [200, 100, 20, 10, 2, 1],\n      );\n\n      await qEqual(\n        users.where().sortByAdminDesc().adminProperty(),\n        [true, true, true, true, false, false],\n      );\n    });\n\n    isarTest('.sortBy().thenBy()', () async {\n      await qEqual(\n        users.where().sortByName().thenByAge(),\n        [\n          UserModel.fill('a', 100, true),\n          UserModel.fill('a', 200, true),\n          UserModel.fill('b', 10, true),\n          UserModel.fill('b', 20, false),\n          UserModel.fill('c', 1, false),\n          UserModel.fill('c', 2, true),\n        ],\n      );\n\n      await qEqual(\n        users.where().sortByAge().thenByName(),\n        [\n          UserModel.fill('c', 1, false),\n          UserModel.fill('c', 2, true),\n          UserModel.fill('b', 10, true),\n          UserModel.fill('b', 20, false),\n          UserModel.fill('a', 100, true),\n          UserModel.fill('a', 200, true),\n        ],\n      );\n\n      await qEqual(\n        users.where().sortByAdmin().thenByName().thenByAge(),\n        [\n          UserModel.fill('b', 20, false),\n          UserModel.fill('c', 1, false),\n          UserModel.fill('a', 100, true),\n          UserModel.fill('a', 200, true),\n          UserModel.fill('b', 10, true),\n          UserModel.fill('c', 2, true),\n        ],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/query/where_sort_distinct_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'where_sort_distinct_test.g.dart';\n\n@collection\nclass TestModel {\n  Id? id;\n\n  @Index()\n  int? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is TestModel && other.id == id && other.value == value;\n  }\n}\n\nvoid main() {\n  group('Where sort distinct', () {\n    late Isar isar;\n    late IsarCollection<TestModel> col;\n\n    setUp(() async {\n      isar = await openTempIsar([TestModelSchema]);\n      col = isar.testModels;\n\n      await isar.writeTxn(() async {\n        for (var i = 0; i <= 2; i++) {\n          final obj = TestModel()..value = i;\n          obj.id = await col.put(obj);\n        }\n        for (var i = 2; i >= 0; i--) {\n          final obj = TestModel()..value = i;\n          obj.id = await col.put(obj);\n        }\n        await col.put(TestModel()..value = null);\n      });\n    });\n\n    isarTest('.any()', () async {\n      await qEqual(\n        col.where().anyValue().valueProperty(),\n        [null, 0, 0, 1, 1, 2, 2],\n      );\n\n      await qEqual(\n        col.where(distinct: true).anyValue().valueProperty(),\n        [null, 0, 1, 2],\n      );\n\n      await qEqual(\n        col.where(sort: Sort.desc).anyValue().valueProperty(),\n        [2, 2, 1, 1, 0, 0, null],\n      );\n\n      await qEqual(\n        col.where(sort: Sort.desc, distinct: true).anyValue().valueProperty(),\n        [2, 1, 0, null],\n      );\n    });\n\n    isarTest('.notEqualTo()', () async {\n      await qEqual(\n        col.where().valueNotEqualTo(1).valueProperty(),\n        [null, 0, 0, 2, 2],\n      );\n\n      await qEqual(\n        col.where(distinct: true).valueNotEqualTo(1).valueProperty(),\n        [null, 0, 2],\n      );\n\n      await qEqual(\n        col.where(sort: Sort.desc).valueNotEqualTo(1).valueProperty(),\n        [2, 2, 0, 0, null],\n      );\n\n      await qEqual(\n        col\n            .where(sort: Sort.desc, distinct: true)\n            .valueNotEqualTo(1)\n            .valueProperty(),\n        [2, 0, null],\n      );\n    });\n\n    isarTest('.isNotNull()', () async {\n      await qEqual(\n        col.where().valueIsNotNull().valueProperty(),\n        [0, 0, 1, 1, 2, 2],\n      );\n\n      await qEqual(\n        col.where(distinct: true).valueIsNotNull().valueProperty(),\n        [0, 1, 2],\n      );\n\n      await qEqual(\n        col.where(sort: Sort.desc).valueIsNotNull().valueProperty(),\n        [2, 2, 1, 1, 0, 0],\n      );\n\n      await qEqual(\n        col\n            .where(sort: Sort.desc, distinct: true)\n            .valueIsNotNull()\n            .valueProperty(),\n        [2, 1, 0],\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/regression/issue_235_rename_field_test.dart",
    "content": "import 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'issue_235_rename_field_test.g.dart';\n\n@collection\n@Name('Collection')\nclass Col1 {\n  Col1({\n    required this.id,\n    this.number = 22,\n    this.numberText3 = 'Default Value3',\n    this.numberText2 = 'Default Value2',\n    this.numberText1 = 'Default Value1',\n  });\n  Id? id;\n\n  int number;\n\n  String numberText3;\n  String numberText2;\n  String numberText1;\n}\n\n@collection\n@Name('Collection')\nclass Col2 {\n  Col2({\n    required this.id,\n    this.number,\n    this.numberText3,\n    this.numberText22,\n    this.numberText1,\n  });\n  Id id;\n\n  int? number;\n\n  String? numberText3;\n  String? numberText22;\n  String? numberText1;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    return other is Col2 &&\n        other.id == id &&\n        other.number == number &&\n        other.numberText3 == numberText3 &&\n        other.numberText22 == numberText22 &&\n        other.numberText1 == numberText1;\n  }\n}\n\nvoid main() {\n  isarTest('Regression 235 Rename field', () async {\n    final isar1 = await openTempIsar([Col1Schema]);\n    await isar1.tWriteTxn(() {\n      return isar1.col1s.tPut(Col1(id: 5));\n    });\n    expect(await isar1.close(), true);\n\n    final isar2 = await openTempIsar([Col2Schema], name: isar1.name);\n    final existing = await isar2.col2s.tGet(5);\n    expect(\n      existing,\n      Col2(\n        id: 5,\n        number: 22,\n        numberText3: 'Default Value3',\n        numberText1: 'Default Value1',\n      ),\n    );\n\n    final newObj = Col2(\n      id: 5,\n      number: 111,\n      numberText3: 'New Value3',\n      numberText1: 'New Value1',\n      numberText22: 'New Value22',\n    );\n    await isar2.tWriteTxn(() {\n      return isar2.col2s.tPut(newObj);\n    });\n    expect(await isar2.col2s.tGet(5), newObj);\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/schema_test.dart",
    "content": "// ignore_for_file: type_annotate_public_apis\n\nimport 'dart:typed_data';\n\nimport 'package:isar/isar.dart';\n\npart 'schema_test.g.dart';\n\n@collection\nclass SchemaTestModel {\n  Id? id;\n\n  @Name('renamedField')\n  @Index(unique: true)\n  late bool someField;\n\n  @Name('renamedGetter')\n  @Index(unique: true, replace: true)\n  bool get someGetter => false;\n\n  @ignore\n  late bool someOtherField;\n\n  @ignore\n  bool get someOtherGetter => false;\n\n  @Index()\n  late bool boolField;\n\n  @Index()\n  bool get boolGetter => false;\n\n  late byte byteField;\n\n  byte get byteGetter => 2;\n\n  late short intField;\n\n  short get intGetter => 0;\n\n  late int longField;\n\n  int get longGetter => 0;\n\n  late float floatField;\n\n  float get floatGetter => 0;\n\n  late double doubleField;\n\n  double get doubleGetter => 0;\n\n  @Index(name: 'stringFieldValue', type: IndexType.value)\n  @Index(name: 'stringFieldHashed')\n  @Index(name: 'stringFieldCaseSensitive')\n  @Index(name: 'stringFieldCaseInsensitive', caseSensitive: false)\n  late String stringField;\n\n  @Index(name: 'stringGetterValue', type: IndexType.value)\n  @Index(name: 'stringGetterHashed')\n  @Index(name: 'stringGetterCaseSensitive')\n  @Index(name: 'stringGetterCaseInsensitive', caseSensitive: false)\n  String get stringGetter => '';\n\n  @Index(name: 'boolListFieldValue', type: IndexType.value)\n  @Index(name: 'boolListFieldHash')\n  late List<bool> boolListField;\n\n  @Index(name: 'boolListGetterValue', type: IndexType.value)\n  @Index(name: 'boolListGetterHash')\n  List<bool> get boolListGetter => [];\n\n  @Index()\n  late List<byte> bytesField;\n\n  @Index()\n  List<byte> get bytesGetter => Uint8List(1);\n\n  late List<short> intListField;\n\n  List<short> get intListGetter => [];\n\n  late List<int> longListField;\n\n  List<int> get longListGetter => [];\n\n  late List<float> floatListField;\n\n  List<float> get floatListGetter => [];\n\n  late List<double> doubleListField;\n\n  List<double> get doubleListGetter => [];\n\n  @Index(name: 'stringListFieldValue', type: IndexType.value)\n  @Index(name: 'stringListFieldHashed')\n  @Index(name: 'stringListFieldHashedElements', type: IndexType.hashElements)\n  @Index(name: 'stringListFieldCaseSensitive')\n  @Index(name: 'stringListFieldCaseInsensitive', caseSensitive: false)\n  late List<String> stringListField;\n\n  @Index(\n    name: 'compositeField1',\n    composite: [\n      CompositeIndex('boolField'),\n    ],\n  )\n  @Index(\n    name: 'compositeField2',\n    composite: [\n      CompositeIndex('boolField'),\n      CompositeIndex('intField'),\n    ],\n  )\n  @Index(\n    name: 'compositeFieldCSCS',\n    composite: [CompositeIndex('stringField')],\n  )\n  @Index(\n    name: 'compositeFieldCICS',\n    caseSensitive: false,\n    composite: [CompositeIndex('stringField')],\n  )\n  @Index(\n    name: 'compositeFieldCSCI',\n    composite: [\n      CompositeIndex('stringField', caseSensitive: false),\n    ],\n  )\n  @Index(\n    name: 'compositeFieldCICI',\n    caseSensitive: false,\n    composite: [\n      CompositeIndex('stringField', caseSensitive: false),\n    ],\n  )\n  @Index(\n    name: 'compositeFieldHashed',\n    composite: [CompositeIndex('stringField')],\n  )\n  @Index(\n    name: 'compositeFieldValue',\n    composite: [CompositeIndex('stringField', type: IndexType.value)],\n  )\n  late String compositeField;\n\n  @Index(\n    name: 'compositeGetter1',\n    composite: [\n      CompositeIndex('boolGetter'),\n    ],\n  )\n  @Index(\n    name: 'compositeGetter2',\n    composite: [\n      CompositeIndex('boolGetter'),\n      CompositeIndex('intGetter'),\n    ],\n  )\n  @Index(\n    name: 'compositeGetterCSCS',\n    composite: [CompositeIndex('stringGetter')],\n  )\n  @Index(\n    name: 'compositeGetterCICS',\n    caseSensitive: false,\n    composite: [CompositeIndex('stringGetter')],\n  )\n  @Index(\n    name: 'compositeGetterCSCI',\n    composite: [\n      CompositeIndex('stringGetter', caseSensitive: false),\n    ],\n  )\n  @Index(\n    name: 'compositeGetterCICI',\n    caseSensitive: false,\n    composite: [\n      CompositeIndex('stringGetter', caseSensitive: false),\n    ],\n  )\n  @Index(\n    name: 'compositeGetterHashed',\n    composite: [CompositeIndex('stringGetter')],\n  )\n  @Index(\n    name: 'compositeGetterValue',\n    composite: [\n      CompositeIndex('stringGetter', type: IndexType.value),\n    ],\n  )\n  String get compositeGetter => '';\n\n  var link = IsarLink<SchemaTestModel>();\n\n  var links = IsarLinks<SchemaTestModel>();\n\n  @Name('renamedLink')\n  var otherLink = IsarLink<SchemaTestModel>();\n\n  @Name('renamedLinks')\n  var otherLinks = IsarLinks<SchemaTestModel>();\n}\n\n@collection\nclass $Dollar$Model {\n  Id? $dollar$id;\n\n  @Index(unique: true)\n  late bool $dollar$Field;\n\n  @Index()\n  bool get $dollar$Getter => false;\n\n  final $dollar$Link = IsarLink<$Dollar$Model>();\n\n  final $dollar$Links = IsarLinks<$Dollar$Model>();\n}\n\nvoid main() {\n  /*isarTest('DollarModel Schema test', () {\n    final schemaJson = jsonDecode($Dollar$ModelSchema.schema);\n    expect(schemaJson, {\n      'name': r'$Dollar$Model',\n      'idName': r'$dollar$id',\n      'properties': [\n        {'name': r'$dollar$Field', 'type': 'Bool'},\n        {'name': r'$dollar$Getter', 'type': 'Bool'}\n      ],\n      'indexes': [\n        {\n          'name': r'$dollar$Field',\n          'unique': true,\n          'replace': false,\n          'properties': [\n            {'name': r'$dollar$Field', 'type': 'Value', 'caseSensitive': false}\n          ]\n        },\n        {\n          'name': r'$dollar$Getter',\n          'unique': false,\n          'replace': false,\n          'properties': [\n            {'name': r'$dollar$Getter', 'type': 'Value', 'caseSensitive': false}\n          ]\n        }\n      ],\n      'links': [\n        {'name': r'$dollar$Link', 'target': r'$Dollar$Model', 'single': true},\n        {'name': r'$dollar$Links', 'target': r'$Dollar$Model', 'single': false}\n      ]\n    });\n  });\n\n  isarTest('Schema test', () {\n    final schemaJson = jsonDecode(SchemaTestModelSchema.schema);\n    expect(\n      schemaJson,\n      {\n        'name': 'SchemaTestModel',\n        'idName': 'id',\n        'properties': [\n          {'name': 'boolField', 'type': 'Bool'},\n          {'name': 'boolGetter', 'type': 'Bool'},\n          {'name': 'boolListField', 'type': 'BoolList'},\n          {'name': 'boolListGetter', 'type': 'BoolList'},\n          {'name': 'byteField', 'type': 'Byte'},\n          {'name': 'byteGetter', 'type': 'Byte'},\n          {'name': 'bytesField', 'type': 'ByteList'},\n          {'name': 'bytesField2', 'type': 'ByteList'},\n          {'name': 'bytesGetter', 'type': 'ByteList'},\n          {'name': 'bytesGetter2', 'type': 'ByteList'},\n          {'name': 'compositeField', 'type': 'String'},\n          {'name': 'compositeGetter', 'type': 'String'},\n          {'name': 'doubleField', 'type': 'Double'},\n          {'name': 'doubleGetter', 'type': 'Double'},\n          {'name': 'doubleListField', 'type': 'DoubleList'},\n          {'name': 'doubleListGetter', 'type': 'DoubleList'},\n          {'name': 'floatField', 'type': 'Float'},\n          {'name': 'floatGetter', 'type': 'Float'},\n          {'name': 'floatListField', 'type': 'FloatList'},\n          {'name': 'floatListGetter', 'type': 'FloatList'},\n          {'name': 'intField', 'type': 'Int'},\n          {'name': 'intGetter', 'type': 'Int'},\n          {'name': 'intListField', 'type': 'IntList'},\n          {'name': 'intListGetter', 'type': 'IntList'},\n          {'name': 'longField', 'type': 'Long'},\n          {'name': 'longGetter', 'type': 'Long'},\n          {'name': 'longListField', 'type': 'LongList'},\n          {'name': 'longListGetter', 'type': 'LongList'},\n          {'name': 'renamedField', 'type': 'Bool'},\n          {'name': 'renamedGetter', 'type': 'Bool'},\n          {'name': 'stringField', 'type': 'String'},\n          {'name': 'stringGetter', 'type': 'String'},\n          {'name': 'stringListField', 'type': 'StringList'}\n        ],\n        'indexes': [\n          {\n            'name': 'boolField',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'boolField', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'boolGetter',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'boolGetter', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'boolListFieldHash',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'boolListField', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'boolListFieldValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'boolListField', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'boolListGetterHash',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'boolListGetter', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'boolListGetterValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'boolListGetter',\n                'type': 'Value',\n                'caseSensitive': false\n              }\n            ]\n          },\n          {\n            'name': 'bytesField',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'bytesField', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'bytesField2',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'bytesField2', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'bytesGetter',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'bytesGetter', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'bytesGetter2',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'bytesGetter2', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeField1',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'compositeField', 'type': 'Hash', 'caseSensitive': true},\n              {'name': 'boolField', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeField2',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'compositeField', 'type': 'Hash', 'caseSensitive': true},\n              {'name': 'boolField', 'type': 'Value', 'caseSensitive': false},\n              {'name': 'intField', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeFieldCICI',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeField',\n                'type': 'Hash',\n                'caseSensitive': false\n              },\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeFieldCICS',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeField',\n                'type': 'Hash',\n                'caseSensitive': false\n              },\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeFieldCSCI',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'compositeField', 'type': 'Hash', 'caseSensitive': true},\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeFieldCSCS',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'compositeField', 'type': 'Hash', 'caseSensitive': true},\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeFieldHashed',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'compositeField', 'type': 'Hash', 'caseSensitive': true},\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeFieldValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'compositeField', 'type': 'Hash', 'caseSensitive': true},\n              {'name': 'stringField', 'type': 'Value', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeGetter1',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': true\n              },\n              {'name': 'boolGetter', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeGetter2',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': true\n              },\n              {'name': 'boolGetter', 'type': 'Value', 'caseSensitive': false},\n              {'name': 'intGetter', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeGetterCICI',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': false\n              },\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeGetterCICS',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': false\n              },\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeGetterCSCI',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': true\n              },\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'compositeGetterCSCS',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': true\n              },\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeGetterHashed',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': true\n              },\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'compositeGetterValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'compositeGetter',\n                'type': 'Hash',\n                'caseSensitive': true\n              },\n              {'name': 'stringGetter', 'type': 'Value', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'renamedField',\n            'unique': true,\n            'replace': false,\n            'properties': [\n              {'name': 'renamedField', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'renamedGetter',\n            'unique': true,\n            'replace': true,\n            'properties': [\n              {'name': 'renamedGetter', 'type': 'Value', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'stringFieldCaseInsensitive',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'stringFieldCaseSensitive',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringFieldHashed',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringFieldValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringField', 'type': 'Value', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringGetterCaseInsensitive',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': false}\n            ]\n          },\n          {\n            'name': 'stringGetterCaseSensitive',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringGetterHashed',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringGetter', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringGetterValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringGetter', 'type': 'Value', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringListFieldCaseInsensitive',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'stringListField',\n                'type': 'Hash',\n                'caseSensitive': false\n              }\n            ]\n          },\n          {\n            'name': 'stringListFieldCaseSensitive',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringListField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringListFieldHashed',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {'name': 'stringListField', 'type': 'Hash', 'caseSensitive': true}\n            ]\n          },\n          {\n            'name': 'stringListFieldHashedElements',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'stringListField',\n                'type': 'HashElements',\n                'caseSensitive': true\n              }\n            ]\n          },\n          {\n            'name': 'stringListFieldValue',\n            'unique': false,\n            'replace': false,\n            'properties': [\n              {\n                'name': 'stringListField',\n                'type': 'Value',\n                'caseSensitive': true\n              }\n            ]\n          }\n        ],\n        'links': [\n          {'name': 'link', 'target': 'SchemaTestModel', 'single': true},\n          {'name': 'links', 'target': 'SchemaTestModel', 'single': false},\n          {'name': 'renamedLink', 'target': 'SchemaTestModel', 'single': true},\n          {'name': 'renamedLinks', 'target': 'SchemaTestModel', 'single': false}\n        ]\n      },\n    );\n  });*/\n}\n"
  },
  {
    "path": "packages/isar_test/test/stress/long_string_test.dart",
    "content": "import 'dart:math';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'long_string_test.g.dart';\n\n@collection\nclass StringModel {\n  StringModel({\n    this.string,\n    this.stringList,\n  });\n\n  Id? id = Isar.autoIncrement;\n\n  String? string;\n\n  List<String>? stringList;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is StringModel &&\n      string == other.string &&\n      listEquals(stringList, other.stringList);\n}\n\nString _randomStr(int length) {\n  final rand = Random();\n  final runes = <int>[];\n  for (var i = 0; i < length; i++) {\n    runes.add(0x10000 + rand.nextInt(0x10000));\n  }\n  return String.fromCharCodes(runes);\n}\n\nvoid main() {\n  group('Long String', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([StringModelSchema]);\n    });\n\n    isarTest('Single', () async {\n      final models = <StringModel>[\n        for (var i = 0; i < 100; i++)\n          StringModel(\n            string: '${_randomStr(50000)}test$i${_randomStr(50000)}',\n          ),\n      ];\n      await isar.tWriteTxn(() async {\n        await isar.stringModels.tPutAll(models);\n      });\n\n      await qEqual(isar.stringModels.where(), models);\n\n      await qEqual(\n        isar.stringModels.filter().stringContains('test75'),\n        [models[75]],\n      );\n      await qEqual(\n        isar.stringModels.filter().stringMatches('*test66*'),\n        [models[66]],\n      );\n    });\n\n    isarTest('List', () async {\n      final models = <StringModel>[\n        for (var i = 0; i < 10; i++)\n          StringModel(\n            stringList: [\n              for (var j = 0; j < 100; j++)\n                '${_randomStr(10000)}test${i}_$j${_randomStr(10000)}',\n            ],\n          ),\n      ];\n      await isar.tWriteTxn(() async {\n        await isar.stringModels.tPutAll(models);\n      });\n\n      await qEqual(isar.stringModels.where(), models);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/stress/twitter_test.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\nFuture<List<Tweet>> downloadTweets(String dir, int index) async {\n  final i = index < 10 ? '0$index' : '$index';\n  final file = File('$dir/tweets$i.json');\n\n  late String jsonStr;\n  if (file.existsSync()) {\n    jsonStr = await file.readAsString();\n  } else {\n    final uri = Uri.parse(\n      'https://raw.githubusercontent.com/isar/testdata/main/twitter$i.json',\n    );\n    final request = await HttpClient().getUrl(uri);\n    final response = await request.close();\n    jsonStr = await response.transform(utf8.decoder).join();\n    await file.writeAsString(jsonStr);\n  }\n\n  final json = jsonDecode(jsonStr) as List<dynamic>;\n  return json.map((e) => Tweet.fromJson(e as Map<String, dynamic>)).toList();\n}\n\nvoid main() async {\n  group(\n    'Twitter Stress',\n    () {\n      late Isar isar;\n      late IsarCollection<Tweet> col;\n\n      setUpAll(() async {\n        isar = await openTempIsar(\n          [TweetSchema],\n          closeAutomatically: false,\n        );\n        col = isar.collection<Tweet>();\n\n        for (var i = 0; i < 100; i++) {\n          final tweets = await downloadTweets(isar.directory!, i);\n          await isar.tWriteTxn(() async {\n            await col.tPutAll(tweets);\n          });\n        }\n      });\n\n      tearDownAll(() => isar.close(deleteFromDisk: true));\n\n      isarTest('Aggregation', () async {\n        expect(await col.where().tCount(), 500000);\n        expect(await col.where().favoriteCountProperty().tSum(), 307278);\n        expect(await col.where().favoriteCountProperty().tAverage(), 0.614556);\n        expect(await col.where().favoriteCountProperty().tMin(), 0);\n        expect(await col.where().favoriteCountProperty().tMax(), 2317);\n\n        expect(\n          (await col.where().createdAtProperty().tMin())!.toUtc(),\n          DateTime.utc(2015, 4, 23, 9, 10, 58),\n        );\n        expect(\n          (await col.where().createdAtProperty().tMax())!.toUtc(),\n          DateTime.utc(2015, 6, 18, 10, 1, 57),\n        );\n      });\n\n      isarTest('Distinct', () async {\n        await qEqualSet(col.where().distinctByLang().langProperty(), [\n          'en', 'it', 'de', 'fr', 'pt', 'und', 'es', 'qme', 'qht', //\n          'hu', 'ja', 'et', 'tl', 'eu', 'pl', 'ht', 'in', 'lt', 'ar', //\n          'ca', 'ru', 'el', 'ro', 'uk', 'sl', 'cy', 'no', 'nl', 'sv', //\n          'fi', 'zh', 'tr', 'cs', 'lv', 'hi', 'is', 'da', 'bg', 'vi', //\n          'ko', 'fa', 'th', 'sr', 'ne', 'ur', 'iw'\n        ]);\n      });\n\n      isarTest('Sort by', () async {\n        final query = col\n            .where()\n            .sortByFavoriteCount()\n            .thenByLang()\n            .thenByFullText()\n            .limit(5)\n            .isarIdProperty();\n        await qEqual(query, [458669, 441027, 368275, 222021, 368289]);\n      });\n\n      isarTest('Query', () async {\n        final complexQuery = col\n            .filter()\n            .not()\n            .group(\n              (q) => q\n                  .placeIsNull()\n                  .or()\n                  .entitiesIsNull()\n                  .or()\n                  .coordinatesIsNull(),\n            )\n            .not()\n            .inReplyToScreenNameIsNull()\n            .entities(\n              (q) => q\n                  .hashtagsLengthGreaterThan(0)\n                  .urlsLengthGreaterThan(0)\n                  .mediaLengthGreaterThan(0)\n                  .userMentionsLengthGreaterThan(0),\n            )\n            .idStrProperty();\n        await qEqual(complexQuery, [\n          '592572613462986752',\n          '596576703285174272',\n          '597445810696126464',\n          '602584278883553280'\n        ]);\n      });\n    },\n    timeout: const Timeout(Duration(minutes: 10)),\n  );\n}\n"
  },
  {
    "path": "packages/isar_test/test/transaction_test.dart",
    "content": "// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes\n\nimport 'dart:async';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'transaction_test.g.dart';\n\n@collection\nclass Model {\n  Model([this.id = Isar.autoIncrement]);\n\n  Id? id;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) => other is Model && id == other.id;\n}\n\nvoid main() {\n  group('Transaction', () {\n    late Isar isar;\n\n    setUp(() async {\n      isar = await openTempIsar([ModelSchema]);\n    });\n\n    isarTest('Sync txn cannot be opened in sync txn', () {\n      isar.txnSync(() {\n        expect(\n          () => isar.txnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        expect(\n          () => isar.writeTxnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n\n      isar.writeTxnSync(() {\n        expect(\n          () => isar.txnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        expect(\n          () => isar.writeTxnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n    });\n\n    isarTest('Sync txn cannot be opened in async txn', () async {\n      await isar.txn(() async {\n        expect(\n          () => isar.txnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        expect(\n          () => isar.writeTxnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n\n      await isar.writeTxn(() async {\n        expect(\n          () => isar.txnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        expect(\n          () => isar.writeTxnSync(() {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n    });\n\n    isarTest('Async txn cannot be opened in sync txn', () {\n      isar.txnSync(() {\n        expect(\n          () => isar.txn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        expect(\n          () => isar.writeTxn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n\n      isar.writeTxnSync(() {\n        expect(\n          () => isar.txn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        expect(\n          () => isar.writeTxn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n    });\n\n    isarTest('Async txn cannot be opened in async txn', () async {\n      await isar.txn(() async {\n        await expectLater(\n          () => isar.txn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        await expectLater(\n          () => isar.writeTxn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n\n      await isar.writeTxn(() async {\n        await expectLater(\n          () => isar.txn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n\n        await expectLater(\n          () => isar.writeTxn(() async {}),\n          throwsIsarError('within an active transaction'),\n        );\n      });\n    });\n\n    isarTest('Sync txn can be opened during async write txn', () async {\n      final c = Completer<void>();\n      final txnFuture = isar.writeTxn(() async {\n        await c.future;\n      });\n\n      isar.txnSync(() {\n        c.complete(null);\n      });\n\n      await txnFuture;\n    });\n\n    isarTest('Sync write txn cannot be opened during async write txn',\n        () async {\n      final c = Completer<void>();\n      final txnFuture = isar.writeTxn(() async {\n        await c.future;\n      });\n\n      expect(\n        () => isar.writeTxnSync(() {}),\n        throwsIsarError('write transaction is already in progress'),\n      );\n\n      c.complete();\n      await txnFuture;\n    });\n\n    isarTest('Async write txn can be opened during async write txn', () async {\n      final c = Completer<void>();\n      final _ = isar.writeTxn(() async {\n        await c.future;\n      });\n\n      final txnFuture = isar.writeTxn(() async {});\n\n      c.complete();\n      await txnFuture;\n    });\n\n    isarTest('Sync writing requires sync write txn', () async {\n      expect(\n        () => isar.models.putSync(Model()),\n        throwsIsarError('require an explicit transaction'),\n      );\n\n      await isar.writeTxn(() async {\n        expect(\n          () => isar.models.putSync(Model()),\n          throwsIsarError('require an explicit transaction'),\n        );\n      });\n    });\n\n    isarTest('Async writing requires async write txn', () async {\n      await expectLater(\n        () => isar.models.put(Model()),\n        throwsIsarError('require an explicit transaction'),\n      );\n\n      isar.writeTxnSync(() {\n        expect(\n          () => isar.models.put(Model()),\n          throwsIsarError('require an explicit transaction'),\n        );\n      });\n    });\n\n    isarTest('Async txn is still active', () async {\n      final completer = Completer<void>();\n      final stream = StreamController<Model>.broadcast();\n      await isar.writeTxn(\n        () async => stream.stream.listen((e) {\n          expect(\n            () => isar.models.put(Model()),\n            throwsIsarError('not active anymore'),\n          );\n          completer.complete();\n        }),\n      );\n      stream.add(Model());\n      await completer.future;\n    });\n\n    isarTest('Nested async transactions of different instances', () async {\n      final isar2 = await openTempIsar([ModelSchema]);\n\n      expect(\n        () => isar.txn(() async {\n          await isar2.models.where().findAll();\n        }),\n        throwsIsarError('transaction does not match'),\n      );\n\n      expect(\n        () => isar.writeTxn(() async {\n          await isar2.models.where().findAll();\n        }),\n        throwsIsarError('transaction does not match'),\n      );\n    });\n    isarTestAsync('gets reverted on error', () async {\n      await isar.writeTxn(() => isar.models.put(Model()));\n      await qEqual(isar.models.where(), [Model(1)]);\n\n      Future<void> errorTxn() async {\n        await isar.writeTxn(() async {\n          await isar.models.put(Model(5));\n          await qEqual(isar.models.where(), [Model(1), Model(5)]);\n          throw UnsupportedError('test');\n        });\n      }\n\n      await expectLater(errorTxn(), throwsUnsupportedError);\n      await qEqual(isar.models.where(), [Model(1)]);\n\n      await expectLater(errorTxn(), throwsUnsupportedError);\n      await qEqual(isar.models.where(), [Model(1)]);\n\n      await isar.writeTxn(() => isar.models.put(Model(5)));\n      await qEqual(isar.models.where(), [Model(1), Model(5)]);\n    });\n\n    isarTestSync('gets reverted on error', () {\n      isar.writeTxnSync(() => isar.models.putSync(Model()));\n      qEqual(isar.models.where(), [Model(1)]);\n\n      void errorTxn() {\n        isar.writeTxnSync(() {\n          isar.models.putSync(Model(5));\n          qEqual(isar.models.where(), [Model(1), Model(5)]);\n          throw UnsupportedError('test');\n        });\n      }\n\n      expect(errorTxn, throwsUnsupportedError);\n      qEqual(isar.models.where(), [Model(1)]);\n\n      expectLater(errorTxn, throwsUnsupportedError);\n      qEqual(isar.models.where(), [Model(1)]);\n\n      isar.writeTxnSync(() => isar.models.putSync(Model(5)));\n      qEqual(isar.models.where(), [Model(1), Model(5)]);\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/test/type_models.dart",
    "content": "import 'package:isar/isar.dart';\n\npart 'type_models.g.dart';\n\n@collection\nclass BoolModel {\n  Id? id;\n\n  bool value = false;\n\n  bool? nValue;\n\n  List<bool> list = [];\n\n  List<bool>? nList;\n}\n\n@collection\nclass ByteModel {\n  Id? id;\n\n  byte value = 0;\n\n  List<byte> list = [];\n\n  List<byte>? nList;\n}\n\n@collection\nclass ShortModel {\n  Id? id;\n\n  short value = 0;\n\n  short? nValue;\n\n  List<short> list = [];\n\n  List<short>? nList;\n}\n\n@collection\nclass IntModel {\n  Id? id;\n\n  int value = 0;\n\n  int? nValue;\n\n  List<int> list = [];\n\n  List<int>? nList;\n}\n\n@collection\nclass FloatModel {\n  Id? id;\n\n  float value = 0;\n\n  float? nValue;\n\n  List<float> list = [];\n\n  List<float>? nList;\n}\n\n@collection\nclass DoubleModel {\n  Id? id;\n\n  double value = 0;\n\n  double? nValue;\n\n  List<double> list = [];\n\n  List<double>? nList;\n}\n\n@collection\nclass DateTimeModel {\n  Id? id;\n\n  DateTime value = DateTime.fromMillisecondsSinceEpoch(0);\n\n  DateTime? nValue;\n\n  List<DateTime> list = [];\n\n  List<DateTime>? nList;\n}\n\n@collection\nclass StringModel {\n  Id? id;\n\n  String value = '';\n\n  String? nValue;\n\n  List<String> list = [];\n\n  List<String>? nList;\n}\n\n@embedded\nclass EmbeddedModel {\n  EmbeddedModel([this.value]);\n\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is EmbeddedModel && other.value == value;\n}\n\n@collection\nclass ObjectModel {\n  Id? id;\n\n  EmbeddedModel value = EmbeddedModel();\n\n  EmbeddedModel? nValue;\n\n  List<EmbeddedModel> list = [];\n\n  List<EmbeddedModel>? nList;\n}\n\nenum TestEnum {\n  option1,\n  option2,\n  option3;\n}\n\n@collection\nclass EnumModel {\n  Id? id;\n\n  @Enumerated(EnumType.name)\n  TestEnum value = TestEnum.option1;\n\n  @Enumerated(EnumType.name)\n  TestEnum? nValue;\n\n  @Enumerated(EnumType.name)\n  List<TestEnum> list = [];\n\n  @Enumerated(EnumType.name)\n  List<TestEnum>? nList;\n}\n"
  },
  {
    "path": "packages/isar_test/test/user_model.dart",
    "content": "import 'package:isar/isar.dart';\n\npart 'user_model.g.dart';\n\n@collection\nclass UserModel {\n  UserModel();\n\n  UserModel.fill(this.name, this.age, this.admin);\n  Id? id;\n\n  @Index()\n  String? name;\n\n  @Index()\n  int? age = 0;\n\n  bool admin = false;\n\n  @override\n  String toString() {\n    return '{name: $name, age: $age, admin: $admin}';\n  }\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) {\n    // ignore: test_types_in_equals\n    return other is UserModel &&\n        name == other.name &&\n        age == other.age &&\n        admin == other.admin;\n  }\n}\n"
  },
  {
    "path": "packages/isar_test/test/watcher_test.dart",
    "content": "import 'dart:async';\n\nimport 'package:isar/isar.dart';\nimport 'package:isar_test/isar_test.dart';\nimport 'package:test/test.dart';\n\npart 'watcher_test.g.dart';\n\n@collection\nclass Value {\n  Value(this.id, this.value);\n  Id? id;\n\n  @Index(unique: true)\n  String? value;\n\n  @override\n  // ignore: hash_and_equals\n  bool operator ==(Object other) =>\n      other is Value && id == other.id && value == other.value;\n}\n\nvoid main() {\n  group('Watcher', () {\n    late Isar isar;\n    late IsarCollection<Value> col;\n\n    late Value obj1;\n    late Value obj2;\n    late Value obj3;\n\n    setUp(() async {\n      isar = await openTempIsar([ValueSchema]);\n      col = isar.values;\n\n      obj1 = Value(1, 'Hello');\n      obj2 = Value(2, 'Hi');\n      obj3 = Value(3, 'Test');\n    });\n\n    group('Collection', () {\n      isarTest('.put()', () async {\n        final listener = Listener<void>(col.watchLazy());\n\n        await isar.tWriteTxn(() => col.tPut(obj1));\n        await listener.next;\n\n        await isar.tWriteTxn(() => col.tPut(obj1));\n        await listener.next;\n\n        await listener.done();\n      });\n\n      isarTest('.putAll()', () async {\n        final listener = Listener<void>(col.watchLazy());\n\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2]));\n        await listener.next;\n\n        await isar.tWriteTxn(() => col.tPutAll([obj1]));\n        await listener.next;\n\n        await listener.done();\n      });\n\n      isarTest('.delete()', () async {\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2]));\n\n        final listener = Listener<void>(col.watchLazy());\n\n        await isar.tWriteTxn(() => col.tDelete(1));\n        await listener.next;\n\n        await isar.tWriteTxn(() => col.tDelete(2));\n        await listener.next;\n\n        await listener.done();\n      });\n\n      isarTest('.deleteAll()', () async {\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2]));\n\n        final listener = Listener<void>(col.watchLazy());\n\n        await isar.tWriteTxn(() => col.tDeleteAll([1, 3]));\n        await listener.next;\n\n        await isar.tWriteTxn(() => col.tDeleteAll([2]));\n        await listener.next;\n\n        await listener.done();\n      });\n\n      isarTest('.deleteBy()', () async {\n        await isar.writeTxn(() => col.putAll([obj1, obj2]));\n\n        final listener = Listener<void>(col.watchLazy());\n\n        await isar.writeTxn(() => col.deleteByValue(obj1.value));\n        await listener.next;\n\n        await isar.writeTxn(() => col.deleteByValue(obj2.value));\n        await listener.next;\n\n        await listener.done();\n      });\n\n      isarTest('.deleteAllBy()', () async {\n        await isar.writeTxn(() => col.putAll([obj1, obj2]));\n\n        final listener = Listener<void>(col.watchLazy());\n\n        await isar\n            .writeTxn(() => col.deleteAllByValue([obj1.value, 'something']));\n        await listener.next;\n\n        await isar.writeTxn(() => col.deleteAllByValue([obj2.value]));\n        await listener.next;\n\n        await listener.done();\n      });\n    });\n\n    group('Object', () {\n      isarTest('.put()', () async {\n        final listenerLazy = Listener<void>(col.watchObjectLazy(1));\n        final listener = Listener<Value?>(col.watchObject(2));\n\n        await isar.tWriteTxn(() => col.tPut(obj1));\n        await listenerLazy.next;\n\n        await isar.tWriteTxn(() => col.tPut(obj2));\n        expect(await listener.next, obj2);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.putAll()', () async {\n        final listenerLazy = Listener<void>(col.watchObjectLazy(1));\n        final listener = Listener<Value?>(col.watchObject(2));\n\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj3]));\n        await listenerLazy.next;\n\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2]));\n        await listenerLazy.next;\n        expect(await listener.next, obj2);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.delete()', () async {\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2, obj3]));\n\n        final listenerLazy = Listener<void>(col.watchObjectLazy(1));\n        final listener = Listener<Value?>(col.watchObject(2));\n\n        await isar.tWriteTxn(() => col.tDelete(1));\n        await listenerLazy.next;\n\n        await isar.tWriteTxn(() => col.tDelete(2));\n        expect(await listener.next, null);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.deleteAll()', () async {\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2, obj3]));\n\n        final listenerLazy = Listener<void>(col.watchObjectLazy(1));\n        final listener = Listener<Value?>(col.watchObject(2));\n\n        await isar.tWriteTxn(() => col.tDeleteAll([4, 1]));\n        await listenerLazy.next;\n\n        await isar.tWriteTxn(() => col.tDeleteAll([2, 3]));\n        expect(await listener.next, null);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.deleteBy()', () async {\n        await isar.writeTxn(() => col.putAll([obj1, obj2, obj3]));\n\n        final listenerLazy = Listener<void>(col.watchObjectLazy(1));\n        final listener = Listener<Value?>(col.watchObject(2));\n\n        await isar.writeTxn(() => col.deleteByValue(obj1.value));\n        await listenerLazy.next;\n\n        await isar.writeTxn(() => col.deleteByValue(obj2.value));\n        expect(await listener.next, null);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.deleteAllBy()', () async {\n        await isar.writeTxn(() => col.putAll([obj1, obj2, obj3]));\n\n        final listenerLazy = Listener<void>(col.watchObjectLazy(1));\n        final listener = Listener<Value?>(col.watchObject(2));\n\n        await isar.writeTxn(() => col.deleteAllByValue(['AAA', obj1.value]));\n        await listenerLazy.next;\n\n        await isar\n            .writeTxn(() => col.deleteAllByValue([obj2.value, obj3.value]));\n        expect(await listener.next, null);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n    });\n\n    group('Query', () {\n      isarTest('.put()', () async {\n        final listenerLazy =\n            Listener(col.where().valueEqualTo('Hello').watchLazy());\n        final listener = Listener(col.where().valueEqualTo('Hi').watch());\n\n        await isar.tWriteTxn(() => col.tPut(obj1));\n        await listenerLazy.next;\n\n        await isar.tWriteTxn(() => col.tPut(obj2));\n        expect(await listener.next, [obj2]);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.putAll()', () async {\n        final listenerLazy =\n            Listener(col.filter().valueContains('H').watchLazy());\n        final listener = Listener(col.filter().valueContains('H').watch());\n\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2]));\n        await listenerLazy.next;\n        expect(await listener.next, [obj1, obj2]);\n\n        await isar.tWriteTxn(() => col.tPutAll([obj3]));\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.delete()', () async {\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2, obj3]));\n\n        final listenerLazy =\n            Listener(col.where().valueEqualTo('Hello').watchLazy());\n        final listener = Listener(col.where().valueEqualTo('Hi').watch());\n\n        await isar.tWriteTxn(() => col.tDelete(1));\n        await listenerLazy.next;\n        if (kIsWeb) {\n          expect(await listener.next, [obj2]);\n        }\n\n        await isar.tWriteTxn(() => col.tDelete(2));\n        if (kIsWeb) {\n          await listenerLazy.next;\n        }\n        expect(await listener.next, <dynamic>[]);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.deleteAll()', () async {\n        await isar.tWriteTxn(() => col.tPutAll([obj1, obj2, obj3]));\n\n        final listenerLazy =\n            Listener(col.filter().valueContains('H').watchLazy());\n        final listener = Listener(col.filter().valueContains('H').watch());\n\n        await isar.tWriteTxn(() => col.tDeleteAll([1, 2]));\n        await listenerLazy.next;\n        expect(await listener.next, <dynamic>[]);\n\n        await isar.tWriteTxn(() => col.tDeleteAll([3]));\n        if (kIsWeb) {\n          await listenerLazy.next;\n          expect(await listener.next, <dynamic>[]);\n        }\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.deleteBy()', () async {\n        await isar.writeTxn(() => col.putAll([obj1, obj2, obj3]));\n\n        final listenerLazy =\n            Listener(col.where().valueEqualTo('Hello').watchLazy());\n        final listener = Listener(col.where().valueEqualTo('Hi').watch());\n\n        await isar.writeTxn(() => col.deleteByValue(obj1.value));\n        await listenerLazy.next;\n        if (kIsWeb) {\n          expect(await listener.next, [obj2]);\n        }\n\n        await isar.writeTxn(() => col.deleteByValue(obj2.value));\n        if (kIsWeb) {\n          await listenerLazy.next;\n        }\n        expect(await listener.next, <dynamic>[]);\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n\n      isarTest('.deleteAllByValue()', () async {\n        await isar.writeTxn(() => col.putAll([obj1, obj2, obj3]));\n\n        final listenerLazy =\n            Listener(col.filter().valueContains('H').watchLazy());\n        final listener = Listener(col.filter().valueContains('H').watch());\n\n        await isar\n            .writeTxn(() => col.deleteAllByValue([obj1.value, obj2.value]));\n        await listenerLazy.next;\n        expect(await listener.next, <dynamic>[]);\n\n        await isar.writeTxn(() => col.deleteAllByValue([obj3.value]));\n        if (kIsWeb) {\n          await listenerLazy.next;\n          expect(await listener.next, <dynamic>[]);\n        }\n\n        await listenerLazy.done();\n        await listener.done();\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "packages/isar_test/tool/generate_all_tests.dart",
    "content": "import 'dart:io';\nimport 'package:path/path.dart' as p;\n\nvoid main() {\n  final files = Directory('test')\n      .listSync(recursive: true)\n      .where((FileSystemEntity e) => e is File && e.path.endsWith('_test.dart'))\n      .map((FileSystemEntity e) => e.path)\n      .toList();\n\n  final imports = files.map((String e) {\n    final dartPath = e.replaceAll(p.separator, '/');\n    final name = e.split('.')[0].replaceAll(p.separator, '_');\n    return \"import '../$dartPath' as $name;\";\n  }).join('\\n');\n\n  final calls = files.map((String e) {\n    final content = File(e).readAsStringSync();\n    var call = \"${e.split('.')[0].replaceAll(p.separator, '_')}.main();\";\n    if (e.contains('stress')) {\n      call = 'if (stress) $call';\n    }\n    if (content.startsWith(\"@TestOn('vm')\")) {\n      return 'if (!kIsWeb) $call';\n    } else {\n      return call;\n    }\n  }).join('\\n');\n\n  final code = \"\"\"\n    // ignore_for_file: directives_ordering\n\n    import 'package:isar_test/isar_test.dart';\n    $imports\n\n    void main() {\n      const stress = bool.fromEnvironment('STRESS');\n      $calls\n    }\n\"\"\";\n\n  Directory('integration_test').createSync();\n  File('integration_test${p.separator}all_tests.dart').writeAsStringSync(code);\n}\n"
  },
  {
    "path": "packages/isar_test/tool/generate_long_double_test.dart",
    "content": "import 'dart:io';\n\nvoid main() {\n  final intTestFile = File('test/filter/filter_int_test.dart');\n  final intTest = intTestFile.readAsStringSync();\n  final longTest = intTest\n      .replaceAll('Int', 'Long')\n      .replaceAll('short', 'int')\n      .replaceAll('filter_int_test', 'filter_long_test')\n      .replaceAll('intModels', 'longModels');\n  File('test/filter/filter_long_test.dart').writeAsStringSync(longTest);\n\n  /*final intListTestFile = File('test/filter/filter_int_list_test.dart');\n  final intListTest = intListTestFile.readAsStringSync();\n  final longListTest = intListTest\n      .replaceAll('Int', 'Long')\n      .replaceAll('short', 'int')\n      .replaceAll('filter_int_test', 'filter_long_test')\n      .replaceAll('intModels', 'longModels');\n  File('test/filter/filter_long_list_test.dart')\n      .writeAsStringSync(longListTest);*/\n\n  final floatTestFile = File('test/filter/filter_float_test.dart');\n  final floatTest = floatTestFile.readAsStringSync();\n  final doubleTest = floatTest\n      .replaceAll('Float', 'Double')\n      .replaceAll('float', 'double')\n      .replaceAll('filter_float_test', 'filter_double_test')\n      .replaceAll('floatModels', 'doubleModels');\n  File('test/filter/filter_double_test.dart').writeAsStringSync(doubleTest);\n\n  final floatListTestFile = File('test/filter/filter_float_list_test.dart');\n  final floatListTest = floatListTestFile.readAsStringSync();\n  final doubleListTest = floatListTest\n      .replaceAll('Float', 'Double')\n      .replaceAll('float', 'double')\n      .replaceAll('filter_float_test', 'filter_double_test')\n      .replaceAll('floatModels', 'doubleModels');\n  File('test/filter/filter_double_list_test.dart')\n      .writeAsStringSync(doubleListTest);\n\n// where clauses\n  final whereIntTestFile = File('test/index/where_int_test.dart');\n  final whereIntTest = whereIntTestFile.readAsStringSync();\n  final whereLongTest = whereIntTest\n      .replaceAll('Int', 'Long')\n      .replaceAll('short', 'int')\n      .replaceAll('where_int_test', 'where_long_test')\n      .replaceAll('intModels', 'longModels');\n  File('test/index/where_long_test.dart').writeAsStringSync(whereLongTest);\n\n  /*final whereIntListTestFile = File('test/index/where_int_list_test.dart');\n  final whereIntListTest = whereIntListTestFile.readAsStringSync();\n  final whereLongListTest = whereIntListTest\n      .replaceAll('Int', 'Long')\n      .replaceAll('short', 'int')\n      .replaceAll('where_int_test', 'where_long_test')\n      .replaceAll('intModels', 'longModels');\n  File('test/index/where_long_list_test.dart')\n      .writeAsStringSync(whereLongListTest);*/\n\n  final whereFloatTestFile = File('test/index/where_float_test.dart');\n  final whereFloatTest = whereFloatTestFile.readAsStringSync();\n  final whereDoubleTest = whereFloatTest\n      .replaceAll('Float', 'Double')\n      .replaceAll('float', 'double')\n      .replaceAll('where_float_test', 'where_double_test')\n      .replaceAll('floatModels', 'doubleModels');\n  File('test/index/where_double_test.dart').writeAsStringSync(whereDoubleTest);\n\n  final whereFloatListTestFile = File('test/index/where_float_list_test.dart');\n  final whereFloatListTest = whereFloatListTestFile.readAsStringSync();\n  final whereDoubleListTest = whereFloatListTest\n      .replaceAll('Float', 'Double')\n      .replaceAll('float', 'double')\n      .replaceAll('where_float_test', 'where_double_test')\n      .replaceAll('floatModels', 'doubleModels');\n  File('test/index/where_double_list_test.dart')\n      .writeAsStringSync(whereDoubleListTest);\n}\n"
  },
  {
    "path": "packages/isar_test/web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!--\n    If you are serving your web app in a path other than the root, change the\n    href value below to reflect the base path you are serving from.\n\n    The path provided below has to start and end with a slash \"/\" in order for\n    it to work correctly.\n\n    For more details:\n    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base\n\n    This is a placeholder for base href that will be replaced by the value of\n    the `--base-href` argument provided to `flutter build`.\n  -->\n  <base href=\"$FLUTTER_BASE_HREF\">\n\n  <meta charset=\"UTF-8\">\n  <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n  <meta name=\"description\" content=\"A new Flutter project.\">\n\n  <!-- iOS meta tags & icons -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"isar_test\">\n  <link rel=\"apple-touch-icon\" href=\"icons/Icon-192.png\">\n\n  <!-- Favicon -->\n  <link rel=\"icon\" type=\"image/png\" href=\"favicon.png\"/>\n\n  <title>isar_test</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n</head>\n<body>\n  <!-- This script installs service_worker.js to provide PWA functionality to\n       application. For more information, see:\n       https://developers.google.com/web/fundamentals/primers/service-workers -->\n  <script>\n    var serviceWorkerVersion = null;\n    var scriptLoaded = false;\n    function loadMainDartJs() {\n      if (scriptLoaded) {\n        return;\n      }\n      scriptLoaded = true;\n      var scriptTag = document.createElement('script');\n      scriptTag.src = 'main.dart.js';\n      scriptTag.type = 'application/javascript';\n      document.body.append(scriptTag);\n    }\n\n    if ('serviceWorker' in navigator) {\n      // Service workers are supported. Use them.\n      window.addEventListener('load', function () {\n        // Wait for registration to finish before dropping the <script> tag.\n        // Otherwise, the browser will load the script multiple times,\n        // potentially different versions.\n        var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;\n        navigator.serviceWorker.register(serviceWorkerUrl)\n          .then((reg) => {\n            function waitForActivation(serviceWorker) {\n              serviceWorker.addEventListener('statechange', () => {\n                if (serviceWorker.state == 'activated') {\n                  console.log('Installed new service worker.');\n                  loadMainDartJs();\n                }\n              });\n            }\n            if (!reg.active && (reg.installing || reg.waiting)) {\n              // No active web worker and we have installed or are installing\n              // one for the first time. Simply wait for it to activate.\n              waitForActivation(reg.installing || reg.waiting);\n            } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {\n              // When the app updates the serviceWorkerVersion changes, so we\n              // need to ask the service worker to update.\n              console.log('New service worker available.');\n              reg.update();\n              waitForActivation(reg.installing);\n            } else {\n              // Existing service worker is still good.\n              console.log('Loading app from service worker.');\n              loadMainDartJs();\n            }\n          });\n\n        // If service worker doesn't succeed in a reasonable amount of time,\n        // fallback to plaint <script> tag.\n        setTimeout(() => {\n          if (!scriptLoaded) {\n            console.warn(\n              'Failed to load app from service worker. Falling back to plain <script> tag.',\n            );\n            loadMainDartJs();\n          }\n        }, 4000);\n      });\n    } else {\n      // Service workers not supported. Just drop the <script> tag.\n      loadMainDartJs();\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "packages/isar_test/web/manifest.json",
    "content": "{\n    \"name\": \"isar_test\",\n    \"short_name\": \"isar_test\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#0175C2\",\n    \"theme_color\": \"#0175C2\",\n    \"description\": \"A new Flutter project.\",\n    \"orientation\": \"portrait-primary\",\n    \"prefer_related_applications\": false,\n    \"icons\": [\n        {\n            \"src\": \"icons/Icon-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        }\n    ]\n}\n"
  },
  {
    "path": "packages/isar_test/windows/.gitignore",
    "content": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "packages/isar_test/windows/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(isar_test LANGUAGES CXX)\n\nset(BINARY_NAME \"isar_test\")\n\ncmake_policy(SET CMP0063 NEW)\n\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Configure build options.\nget_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\nif(IS_MULTICONFIG)\n  set(CMAKE_CONFIGURATION_TYPES \"Debug;Profile;Release\"\n    CACHE STRING \"\" FORCE)\nelse()\n  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n      STRING \"Flutter build mode\" FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n      \"Debug\" \"Profile\" \"Release\")\n  endif()\nendif()\n\nset(CMAKE_EXE_LINKER_FLAGS_PROFILE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_SHARED_LINKER_FLAGS_PROFILE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_C_FLAGS_PROFILE \"${CMAKE_C_FLAGS_RELEASE}\")\nset(CMAKE_CXX_FLAGS_PROFILE \"${CMAKE_CXX_FLAGS_RELEASE}\")\n\n# Use Unicode for all projects.\nadd_definitions(-DUNICODE -D_UNICODE)\n\n# Compilation settings that should be applied to most targets.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_17)\n  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd\"4100\")\n  target_compile_options(${TARGET} PRIVATE /EHsc)\n  target_compile_definitions(${TARGET} PRIVATE \"_HAS_EXCEPTIONS=0\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<CONFIG:Debug>:_DEBUG>\")\nendfunction()\n\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\n\n# Flutter library and tool build rules.\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# Application build\nadd_subdirectory(\"runner\")\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# Support files are copied into place next to the executable, so that it can\n# run in place. This is done instead of making a separate bundle (as on Linux)\n# so that building and running from within Visual Studio will work.\nset(BUILD_BUNDLE_DIR \"$<TARGET_FILE_DIR:${BINARY_NAME}>\")\n# Make the \"install\" step default, as it's required to run.\nset(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\ninstall(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n"
  },
  {
    "path": "packages/isar_test/windows/flutter/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\nset(WRAPPER_ROOT \"${EPHEMERAL_DIR}/cpp_client_wrapper\")\n\n# === Flutter Library ===\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/flutter_windows.dll\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/windows/app.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"flutter_export.h\"\n  \"flutter_windows.h\"\n  \"flutter_messenger.h\"\n  \"flutter_plugin_registrar.h\"\n  \"flutter_texture_registrar.h\"\n)\nlist(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND \"${EPHEMERAL_DIR}/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}.lib\")\nadd_dependencies(flutter flutter_assemble)\n\n# === Wrapper ===\nlist(APPEND CPP_WRAPPER_SOURCES_CORE\n  \"core_implementations.cc\"\n  \"standard_codec.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_PLUGIN\n  \"plugin_registrar.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_APP\n  \"flutter_engine.cc\"\n  \"flutter_view_controller.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND \"${WRAPPER_ROOT}/\")\n\n# Wrapper sources needed for a plugin.\nadd_library(flutter_wrapper_plugin STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n)\napply_standard_settings(flutter_wrapper_plugin)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  POSITION_INDEPENDENT_CODE ON)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_link_libraries(flutter_wrapper_plugin PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_plugin PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_plugin flutter_assemble)\n\n# Wrapper sources needed for the runner.\nadd_library(flutter_wrapper_app STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\napply_standard_settings(flutter_wrapper_app)\ntarget_link_libraries(flutter_wrapper_app PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_app PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_app flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nset(PHONY_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/_phony_\")\nset_source_files_properties(\"${PHONY_OUTPUT}\" PROPERTIES SYMBOLIC TRUE)\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}\n    ${CPP_WRAPPER_SOURCES_APP}\n    ${PHONY_OUTPUT}\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat\"\n      windows-x64 $<CONFIG>\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\n"
  },
  {
    "path": "packages/isar_test/windows/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <isar_flutter_libs/isar_flutter_libs_plugin.h>\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n  IsarFlutterLibsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"IsarFlutterLibsPlugin\"));\n}\n"
  },
  {
    "path": "packages/isar_test/windows/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter/plugin_registry.h>\n\n// Registers Flutter plugins.\nvoid RegisterPlugins(flutter::PluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "packages/isar_test/windows/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  isar_flutter_libs\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "packages/isar_test/windows/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(runner LANGUAGES CXX)\n\nadd_executable(${BINARY_NAME} WIN32\n  \"flutter_window.cpp\"\n  \"main.cpp\"\n  \"utils.cpp\"\n  \"win32_window.cpp\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n  \"Runner.rc\"\n  \"runner.exe.manifest\"\n)\napply_standard_settings(${BINARY_NAME})\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"NOMINMAX\")\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n"
  },
  {
    "path": "packages/isar_test/windows/runner/Runner.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE\nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE\nBEGIN\n    \"#include \"\"winres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE\nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_APP_ICON            ICON                    \"resources\\\\app_icon.ico\"\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n#ifdef FLUTTER_BUILD_NUMBER\n#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER\n#else\n#define VERSION_AS_NUMBER 1,0,0\n#endif\n\n#ifdef FLUTTER_BUILD_NAME\n#define VERSION_AS_STRING #FLUTTER_BUILD_NAME\n#else\n#define VERSION_AS_STRING \"1.0.0\"\n#endif\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION VERSION_AS_NUMBER\n PRODUCTVERSION VERSION_AS_NUMBER\n FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#ifdef _DEBUG\n FILEFLAGS VS_FF_DEBUG\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS VOS__WINDOWS32\n FILETYPE VFT_APP\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904e4\"\n        BEGIN\n            VALUE \"CompanyName\", \"dev.isar\" \"\\0\"\n            VALUE \"FileDescription\", \"A new Flutter project.\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"isar_test\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2022 dev.isar. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"isar_test.exe\" \"\\0\"\n            VALUE \"ProductName\", \"isar_test\" \"\\0\"\n            VALUE \"ProductVersion\", VERSION_AS_STRING \"\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1252\n    END\nEND\n\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n"
  },
  {
    "path": "packages/isar_test/windows/runner/flutter_window.cpp",
    "content": "#include \"flutter_window.h\"\n\n#include <optional>\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nFlutterWindow::FlutterWindow(const flutter::DartProject& project)\n    : project_(project) {}\n\nFlutterWindow::~FlutterWindow() {}\n\nbool FlutterWindow::OnCreate() {\n  if (!Win32Window::OnCreate()) {\n    return false;\n  }\n\n  RECT frame = GetClientArea();\n\n  // The size here must match the window dimensions to avoid unnecessary surface\n  // creation / destruction in the startup path.\n  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(\n      frame.right - frame.left, frame.bottom - frame.top, project_);\n  // Ensure that basic setup of the controller was successful.\n  if (!flutter_controller_->engine() || !flutter_controller_->view()) {\n    return false;\n  }\n  RegisterPlugins(flutter_controller_->engine());\n  SetChildContent(flutter_controller_->view()->GetNativeWindow());\n  return true;\n}\n\nvoid FlutterWindow::OnDestroy() {\n  if (flutter_controller_) {\n    flutter_controller_ = nullptr;\n  }\n\n  Win32Window::OnDestroy();\n}\n\nLRESULT\nFlutterWindow::MessageHandler(HWND hwnd, UINT const message,\n                              WPARAM const wparam,\n                              LPARAM const lparam) noexcept {\n  // Give Flutter, including plugins, an opportunity to handle window messages.\n  if (flutter_controller_) {\n    std::optional<LRESULT> result =\n        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,\n                                                      lparam);\n    if (result) {\n      return *result;\n    }\n  }\n\n  switch (message) {\n    case WM_FONTCHANGE:\n      flutter_controller_->engine()->ReloadSystemFonts();\n      break;\n  }\n\n  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);\n}\n"
  },
  {
    "path": "packages/isar_test/windows/runner/flutter_window.h",
    "content": "#ifndef RUNNER_FLUTTER_WINDOW_H_\n#define RUNNER_FLUTTER_WINDOW_H_\n\n#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n\n#include <memory>\n\n#include \"win32_window.h\"\n\n// A window that does nothing but host a Flutter view.\nclass FlutterWindow : public Win32Window {\n public:\n  // Creates a new FlutterWindow hosting a Flutter view running |project|.\n  explicit FlutterWindow(const flutter::DartProject& project);\n  virtual ~FlutterWindow();\n\n protected:\n  // Win32Window:\n  bool OnCreate() override;\n  void OnDestroy() override;\n  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,\n                         LPARAM const lparam) noexcept override;\n\n private:\n  // The project to run.\n  flutter::DartProject project_;\n\n  // The Flutter instance hosted by this window.\n  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;\n};\n\n#endif  // RUNNER_FLUTTER_WINDOW_H_\n"
  },
  {
    "path": "packages/isar_test/windows/runner/main.cpp",
    "content": "#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n#include <windows.h>\n\n#include \"flutter_window.h\"\n#include \"utils.h\"\n\nint APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,\n                      _In_ wchar_t *command_line, _In_ int show_command) {\n  // Attach to console when present (e.g., 'flutter run') or create a\n  // new console when running with a debugger.\n  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {\n    CreateAndAttachConsole();\n  }\n\n  // Initialize COM, so that it is available for use in the library and/or\n  // plugins.\n  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);\n\n  flutter::DartProject project(L\"data\");\n\n  std::vector<std::string> command_line_arguments =\n      GetCommandLineArguments();\n\n  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));\n\n  FlutterWindow window(project);\n  Win32Window::Point origin(10, 10);\n  Win32Window::Size size(1280, 720);\n  if (!window.CreateAndShow(L\"isar_test\", origin, size)) {\n    return EXIT_FAILURE;\n  }\n  window.SetQuitOnClose(true);\n\n  ::MSG msg;\n  while (::GetMessage(&msg, nullptr, 0, 0)) {\n    ::TranslateMessage(&msg);\n    ::DispatchMessage(&msg);\n  }\n\n  ::CoUninitialize();\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "packages/isar_test/windows/runner/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Runner.rc\n//\n#define IDI_APP_ICON                    101\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "packages/isar_test/windows/runner/runner.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n    </windowsSettings>\n  </application>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 10 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n      <!-- Windows 8.1 -->\n      <supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n      <!-- Windows 8 -->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n      <!-- Windows 7 -->\n      <supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n    </application>\n  </compatibility>\n</assembly>\n"
  },
  {
    "path": "packages/isar_test/windows/runner/utils.cpp",
    "content": "#include \"utils.h\"\n\n#include <flutter_windows.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include <iostream>\n\nvoid CreateAndAttachConsole() {\n  if (::AllocConsole()) {\n    FILE *unused;\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stdout)) {\n      _dup2(_fileno(stdout), 1);\n    }\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stderr)) {\n      _dup2(_fileno(stdout), 2);\n    }\n    std::ios::sync_with_stdio();\n    FlutterDesktopResyncOutputStreams();\n  }\n}\n\nstd::vector<std::string> GetCommandLineArguments() {\n  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.\n  int argc;\n  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);\n  if (argv == nullptr) {\n    return std::vector<std::string>();\n  }\n\n  std::vector<std::string> command_line_arguments;\n\n  // Skip the first argument as it's the binary name.\n  for (int i = 1; i < argc; i++) {\n    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));\n  }\n\n  ::LocalFree(argv);\n\n  return command_line_arguments;\n}\n\nstd::string Utf8FromUtf16(const wchar_t* utf16_string) {\n  if (utf16_string == nullptr) {\n    return std::string();\n  }\n  int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr);\n  if (target_length == 0) {\n    return std::string();\n  }\n  std::string utf8_string;\n  utf8_string.resize(target_length);\n  int converted_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, utf8_string.data(),\n      target_length, nullptr, nullptr);\n  if (converted_length == 0) {\n    return std::string();\n  }\n  return utf8_string;\n}\n"
  },
  {
    "path": "packages/isar_test/windows/runner/utils.h",
    "content": "#ifndef RUNNER_UTILS_H_\n#define RUNNER_UTILS_H_\n\n#include <string>\n#include <vector>\n\n// Creates a console for the process, and redirects stdout and stderr to\n// it for both the runner and the Flutter library.\nvoid CreateAndAttachConsole();\n\n// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string\n// encoded in UTF-8. Returns an empty std::string on failure.\nstd::string Utf8FromUtf16(const wchar_t* utf16_string);\n\n// Gets the command line arguments passed in as a std::vector<std::string>,\n// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.\nstd::vector<std::string> GetCommandLineArguments();\n\n#endif  // RUNNER_UTILS_H_\n"
  },
  {
    "path": "packages/isar_test/windows/runner/win32_window.cpp",
    "content": "#include \"win32_window.h\"\n\n#include <flutter_windows.h>\n\n#include \"resource.h\"\n\nnamespace {\n\nconstexpr const wchar_t kWindowClassName[] = L\"FLUTTER_RUNNER_WIN32_WINDOW\";\n\n// The number of Win32Window objects that currently exist.\nstatic int g_active_window_count = 0;\n\nusing EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);\n\n// Scale helper to convert logical scaler values to physical using passed in\n// scale factor\nint Scale(int source, double scale_factor) {\n  return static_cast<int>(source * scale_factor);\n}\n\n// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.\n// This API is only needed for PerMonitor V1 awareness mode.\nvoid EnableFullDpiSupportIfAvailable(HWND hwnd) {\n  HMODULE user32_module = LoadLibraryA(\"User32.dll\");\n  if (!user32_module) {\n    return;\n  }\n  auto enable_non_client_dpi_scaling =\n      reinterpret_cast<EnableNonClientDpiScaling*>(\n          GetProcAddress(user32_module, \"EnableNonClientDpiScaling\"));\n  if (enable_non_client_dpi_scaling != nullptr) {\n    enable_non_client_dpi_scaling(hwnd);\n    FreeLibrary(user32_module);\n  }\n}\n\n}  // namespace\n\n// Manages the Win32Window's window class registration.\nclass WindowClassRegistrar {\n public:\n  ~WindowClassRegistrar() = default;\n\n  // Returns the singleton registar instance.\n  static WindowClassRegistrar* GetInstance() {\n    if (!instance_) {\n      instance_ = new WindowClassRegistrar();\n    }\n    return instance_;\n  }\n\n  // Returns the name of the window class, registering the class if it hasn't\n  // previously been registered.\n  const wchar_t* GetWindowClass();\n\n  // Unregisters the window class. Should only be called if there are no\n  // instances of the window.\n  void UnregisterWindowClass();\n\n private:\n  WindowClassRegistrar() = default;\n\n  static WindowClassRegistrar* instance_;\n\n  bool class_registered_ = false;\n};\n\nWindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;\n\nconst wchar_t* WindowClassRegistrar::GetWindowClass() {\n  if (!class_registered_) {\n    WNDCLASS window_class{};\n    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);\n    window_class.lpszClassName = kWindowClassName;\n    window_class.style = CS_HREDRAW | CS_VREDRAW;\n    window_class.cbClsExtra = 0;\n    window_class.cbWndExtra = 0;\n    window_class.hInstance = GetModuleHandle(nullptr);\n    window_class.hIcon =\n        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));\n    window_class.hbrBackground = 0;\n    window_class.lpszMenuName = nullptr;\n    window_class.lpfnWndProc = Win32Window::WndProc;\n    RegisterClass(&window_class);\n    class_registered_ = true;\n  }\n  return kWindowClassName;\n}\n\nvoid WindowClassRegistrar::UnregisterWindowClass() {\n  UnregisterClass(kWindowClassName, nullptr);\n  class_registered_ = false;\n}\n\nWin32Window::Win32Window() {\n  ++g_active_window_count;\n}\n\nWin32Window::~Win32Window() {\n  --g_active_window_count;\n  Destroy();\n}\n\nbool Win32Window::CreateAndShow(const std::wstring& title,\n                                const Point& origin,\n                                const Size& size) {\n  Destroy();\n\n  const wchar_t* window_class =\n      WindowClassRegistrar::GetInstance()->GetWindowClass();\n\n  const POINT target_point = {static_cast<LONG>(origin.x),\n                              static_cast<LONG>(origin.y)};\n  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);\n  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);\n  double scale_factor = dpi / 96.0;\n\n  HWND window = CreateWindow(\n      window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,\n      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),\n      Scale(size.width, scale_factor), Scale(size.height, scale_factor),\n      nullptr, nullptr, GetModuleHandle(nullptr), this);\n\n  if (!window) {\n    return false;\n  }\n\n  return OnCreate();\n}\n\n// static\nLRESULT CALLBACK Win32Window::WndProc(HWND const window,\n                                      UINT const message,\n                                      WPARAM const wparam,\n                                      LPARAM const lparam) noexcept {\n  if (message == WM_NCCREATE) {\n    auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);\n    SetWindowLongPtr(window, GWLP_USERDATA,\n                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));\n\n    auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);\n    EnableFullDpiSupportIfAvailable(window);\n    that->window_handle_ = window;\n  } else if (Win32Window* that = GetThisFromHandle(window)) {\n    return that->MessageHandler(window, message, wparam, lparam);\n  }\n\n  return DefWindowProc(window, message, wparam, lparam);\n}\n\nLRESULT\nWin32Window::MessageHandler(HWND hwnd,\n                            UINT const message,\n                            WPARAM const wparam,\n                            LPARAM const lparam) noexcept {\n  switch (message) {\n    case WM_DESTROY:\n      window_handle_ = nullptr;\n      Destroy();\n      if (quit_on_close_) {\n        PostQuitMessage(0);\n      }\n      return 0;\n\n    case WM_DPICHANGED: {\n      auto newRectSize = reinterpret_cast<RECT*>(lparam);\n      LONG newWidth = newRectSize->right - newRectSize->left;\n      LONG newHeight = newRectSize->bottom - newRectSize->top;\n\n      SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,\n                   newHeight, SWP_NOZORDER | SWP_NOACTIVATE);\n\n      return 0;\n    }\n    case WM_SIZE: {\n      RECT rect = GetClientArea();\n      if (child_content_ != nullptr) {\n        // Size and position the child window.\n        MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,\n                   rect.bottom - rect.top, TRUE);\n      }\n      return 0;\n    }\n\n    case WM_ACTIVATE:\n      if (child_content_ != nullptr) {\n        SetFocus(child_content_);\n      }\n      return 0;\n  }\n\n  return DefWindowProc(window_handle_, message, wparam, lparam);\n}\n\nvoid Win32Window::Destroy() {\n  OnDestroy();\n\n  if (window_handle_) {\n    DestroyWindow(window_handle_);\n    window_handle_ = nullptr;\n  }\n  if (g_active_window_count == 0) {\n    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();\n  }\n}\n\nWin32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {\n  return reinterpret_cast<Win32Window*>(\n      GetWindowLongPtr(window, GWLP_USERDATA));\n}\n\nvoid Win32Window::SetChildContent(HWND content) {\n  child_content_ = content;\n  SetParent(content, window_handle_);\n  RECT frame = GetClientArea();\n\n  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,\n             frame.bottom - frame.top, true);\n\n  SetFocus(child_content_);\n}\n\nRECT Win32Window::GetClientArea() {\n  RECT frame;\n  GetClientRect(window_handle_, &frame);\n  return frame;\n}\n\nHWND Win32Window::GetHandle() {\n  return window_handle_;\n}\n\nvoid Win32Window::SetQuitOnClose(bool quit_on_close) {\n  quit_on_close_ = quit_on_close;\n}\n\nbool Win32Window::OnCreate() {\n  // No-op; provided for subclasses.\n  return true;\n}\n\nvoid Win32Window::OnDestroy() {\n  // No-op; provided for subclasses.\n}\n"
  },
  {
    "path": "packages/isar_test/windows/runner/win32_window.h",
    "content": "#ifndef RUNNER_WIN32_WINDOW_H_\n#define RUNNER_WIN32_WINDOW_H_\n\n#include <windows.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n\n// A class abstraction for a high DPI-aware Win32 Window. Intended to be\n// inherited from by classes that wish to specialize with custom\n// rendering and input handling\nclass Win32Window {\n public:\n  struct Point {\n    unsigned int x;\n    unsigned int y;\n    Point(unsigned int x, unsigned int y) : x(x), y(y) {}\n  };\n\n  struct Size {\n    unsigned int width;\n    unsigned int height;\n    Size(unsigned int width, unsigned int height)\n        : width(width), height(height) {}\n  };\n\n  Win32Window();\n  virtual ~Win32Window();\n\n  // Creates and shows a win32 window with |title| and position and size using\n  // |origin| and |size|. New windows are created on the default monitor. Window\n  // sizes are specified to the OS in physical pixels, hence to ensure a\n  // consistent size to will treat the width height passed in to this function\n  // as logical pixels and scale to appropriate for the default monitor. Returns\n  // true if the window was created successfully.\n  bool CreateAndShow(const std::wstring& title,\n                     const Point& origin,\n                     const Size& size);\n\n  // Release OS resources associated with window.\n  void Destroy();\n\n  // Inserts |content| into the window tree.\n  void SetChildContent(HWND content);\n\n  // Returns the backing Window handle to enable clients to set icon and other\n  // window properties. Returns nullptr if the window has been destroyed.\n  HWND GetHandle();\n\n  // If true, closing this window will quit the application.\n  void SetQuitOnClose(bool quit_on_close);\n\n  // Return a RECT representing the bounds of the current client area.\n  RECT GetClientArea();\n\n protected:\n  // Processes and route salient window messages for mouse handling,\n  // size change and DPI. Delegates handling of these to member overloads that\n  // inheriting classes can handle.\n  virtual LRESULT MessageHandler(HWND window,\n                                 UINT const message,\n                                 WPARAM const wparam,\n                                 LPARAM const lparam) noexcept;\n\n  // Called when CreateAndShow is called, allowing subclass window-related\n  // setup. Subclasses should return false if setup fails.\n  virtual bool OnCreate();\n\n  // Called when Destroy is called.\n  virtual void OnDestroy();\n\n private:\n  friend class WindowClassRegistrar;\n\n  // OS callback called by message pump. Handles the WM_NCCREATE message which\n  // is passed when the non-client area is being created and enables automatic\n  // non-client DPI scaling so that the non-client area automatically\n  // responsponds to changes in DPI. All other messages are handled by\n  // MessageHandler.\n  static LRESULT CALLBACK WndProc(HWND const window,\n                                  UINT const message,\n                                  WPARAM const wparam,\n                                  LPARAM const lparam) noexcept;\n\n  // Retrieves a class instance pointer for |window|\n  static Win32Window* GetThisFromHandle(HWND const window) noexcept;\n\n  bool quit_on_close_ = false;\n\n  // window handle for top level window.\n  HWND window_handle_ = nullptr;\n\n  // window handle for hosted content.\n  HWND child_content_ = nullptr;\n};\n\n#endif  // RUNNER_WIN32_WINDOW_H_\n"
  },
  {
    "path": "packages/isar_web/.eslintrc.yml",
    "content": "env:\n  browser: true\n  commonjs: true\n  es2021: true\nextends:\n  - 'eslint:recommended'\n  - 'plugin:@typescript-eslint/recommended'\nparser: '@typescript-eslint/parser'\nparserOptions:\n  ecmaVersion: 12\nplugins:\n  - '@typescript-eslint'\nrules: {}\n"
  },
  {
    "path": "packages/isar_web/.gitignore",
    "content": "node_modules\n.idea\n.vscode\ndist"
  },
  {
    "path": "packages/isar_web/.prettierrc",
    "content": "{\n  \"semi\": false,\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\",\n  \"arrowParens\": \"avoid\"\n}\n"
  },
  {
    "path": "packages/isar_web/README.md",
    "content": "# isar-web\n\nThe web runtime for the Isar database.\n"
  },
  {
    "path": "packages/isar_web/package.json",
    "content": "{\n  \"name\": \"isar\",\n  \"version\": \"2.5.1\",\n  \"description\": \"The web bindings for Isar, a multi-platform database for Dart.\",\n  \"author\": \"Simon Leier\",\n  \"license\": \"Apache-2.0\",\n  \"repository\": \"https://github.com/isar-community/isar-web\",\n  \"homepage\": \"https://isar.dev/\",\n  \"main\": \"dist/index.js\",\n  \"directories\": {\n    \"test\": \"test\"\n  },\n  \"scripts\": {\n    \"build\": \"rm -rf ./out && npx tsc\"\n  },\n  \"devDependencies\": {\n    \"ts-loader\": \"^9.2.6\",\n    \"typescript\": \"^4.5.5\",\n    \"webpack\": \"^5.67.0\",\n    \"webpack-cli\": \"^5.0.0\",\n    \"fast-deep-equal\": \"^3.1.1\",\n    \"broadcast-channel\": \"^4.11.0\"\n  }\n}"
  },
  {
    "path": "packages/isar_web/src/bulk-delete.ts",
    "content": "import { IsarTxn } from './txn'\n\nexport function bulkDelete(\n  txn: IsarTxn,\n  storeName: string,\n  keys: (IDBValidKey | IDBKeyRange)[],\n): Promise<void> {\n  return new Promise((resolve, reject) => {\n    const len = keys.length\n    const lastItem = len - 1\n    if (len === 0) return resolve()\n    const store = txn.txn.objectStore(storeName)\n    for (let i = 0; i < keys.length; i++) {\n      const req = store.delete(keys[i])\n      req.onerror = () => {\n        txn.abort()\n        reject(req.error)\n      }\n      if (i === lastItem) {\n        req.onsuccess = () => {\n          resolve()\n        }\n      }\n    }\n  })\n}\n\nexport function bulkDeleteByIndex(\n  txn: IsarTxn,\n  storeName: string,\n  indexName: string,\n  keys: IDBValidKey[],\n): Promise<IDBValidKey[]> {\n  if (keys.length === 0) return Promise.resolve([])\n  return new Promise((resolve, reject) => {\n    const store = txn.txn.objectStore(storeName)\n    const index = store.index(indexName)\n\n    const primaryKeys: IDBValidKey[] = []\n    for (var i = 0; i < keys.length; i++) {\n      const indexReq = index.getAllKeys(keys[i])\n      const isLast = i === keys.length - 1\n      indexReq.onsuccess = () => {\n        primaryKeys.push(...indexReq.result)\n        if (isLast) {\n          bulkDelete(txn, storeName, primaryKeys).then(\n            () => resolve(primaryKeys),\n            reject,\n          )\n        }\n      }\n      indexReq.onerror = () => {\n        txn.abort()\n        reject(indexReq.error)\n      }\n    }\n  })\n}\n"
  },
  {
    "path": "packages/isar_web/src/collection.ts",
    "content": "import { bulkDelete, bulkDeleteByIndex } from './bulk-delete'\nimport { idName, IsarInstance } from './instance'\nimport { IsarLink } from './link'\nimport { IndexSchema, IsarType, Schema } from './schema'\nimport { IsarTxn } from './txn'\nimport { IsarWatchable } from './watcher'\n\ninterface UniqueIndex {\n  readonly name: string\n  readonly accessors: string[]\n}\n\nexport type IndexKey = string | number | IndexKey[]\n\nexport class IsarCollection<OBJ> extends IsarWatchable<OBJ> {\n  readonly isar: IsarInstance\n  readonly name: string\n  private readonly uniqueIndexes: ReadonlyArray<UniqueIndex>\n  private readonly links: ReadonlyArray<IsarLink>\n  // only backlinks that don't target this collection\n  private readonly backlinkStoreNames: ReadonlyArray<string>\n  private readonly multiEntryIndexes: string[]\n  private readonly indexKeyPaths = new Map<string, string[]>()\n\n  constructor(\n    isar: IsarInstance,\n    schema: Schema,\n    backlinkStoreNames: string[],\n  ) {\n    super()\n    this.isar = isar\n    this.name = schema.name\n    this.uniqueIndexes = schema.indexes\n      .filter(i => i.unique)\n      .map(i => ({\n        name: i.name,\n        accessors: i.properties.map(p => p.name),\n      }))\n    this.links = schema.links.map(\n      l => new IsarLink(isar, l.name, schema.name, l.target),\n    )\n    this.backlinkStoreNames = backlinkStoreNames\n    this.multiEntryIndexes = schema.indexes\n      .filter(i => IndexSchema.isIndexMultiEntry(schema, i))\n      .map(i => i.name)\n    this.indexKeyPaths = new Map(\n      schema.indexes.map(i => [i.name, i.properties.map(p => p.name)]),\n    )\n  }\n\n  getLink(name: string): IsarLink | undefined {\n    return this.links.find(l => l.name === name)\n  }\n\n  getIndexKeyPath(indexName: string): string[] {\n    return this.indexKeyPaths.get(indexName)!\n  }\n\n  isMultiEntryIndex(indexName: string): boolean {\n    return this.multiEntryIndexes.includes(indexName)\n  }\n\n  get(txn: IsarTxn, id: number): Promise<OBJ | undefined> {\n    let store = txn.txn.objectStore(this.name)\n    return new Promise((resolve, reject) => {\n      let req = store.get(id)\n      req.onsuccess = () => {\n        const object = req.result\n        if (object) {\n          object[idName] = id\n        }\n        resolve(object)\n      }\n      req.onerror = () => {\n        reject(req.error)\n      }\n    })\n  }\n\n  getAll(txn: IsarTxn, ids: number[]): Promise<(OBJ | undefined)[]> {\n    return new Promise((resolve, reject) => {\n      const store = txn.txn.objectStore(this.name)\n      const results: (OBJ | undefined)[] = []\n      for (let i = 0; i < ids.length; i++) {\n        const id = ids[i]\n        const req = store.get(id)\n        req.onsuccess = () => {\n          const object = req.result\n          if (object) {\n            object[idName] = id\n          }\n          results.push(object)\n          if (results.length == ids.length) {\n            resolve(results)\n          }\n        }\n        req.onerror = () => {\n          reject(req.error)\n        }\n      }\n    })\n  }\n\n  getAllByIndex(\n    txn: IsarTxn,\n    indexName: string,\n    keys: IndexKey[],\n  ): Promise<(OBJ | undefined)[]> {\n    if (keys.length === 0) {\n      return Promise.resolve([])\n    }\n\n    keys.sort(indexedDB.cmp)\n    return new Promise((resolve, reject) => {\n      const store = txn.txn.objectStore(this.name)\n      const results: (OBJ | undefined)[] = []\n      const cursorReq = store.index(indexName).openCursor()\n      cursorReq.onsuccess = () => {\n        const cursor = cursorReq.result\n        if (cursor) {\n          const object = cursor.value\n          if (results.length > 0 || cursor.key === keys[0]) {\n            if (object) {\n              object[idName] = cursor.primaryKey\n              results.push(object)\n            } else {\n              results.push(undefined)\n            }\n          }\n          if (results.length == keys.length) {\n            resolve(results)\n          } else {\n            cursor.continue(keys[results.length])\n          }\n        } else {\n          resolve([])\n        }\n      }\n      cursorReq.onerror = e => {\n        reject(e)\n      }\n    })\n  }\n\n  putAll(txn: IsarTxn, objects: OBJ[]): Promise<number[]> {\n    let store = txn.txn.objectStore(this.name)\n    return new Promise((resolve, reject) => {\n      const ids: (number | undefined)[] = []\n      const changeSet = txn.getChangeSet(this.name)\n      for (let i = 0; i < objects.length; i++) {\n        const object = objects[i] as any\n        const id = object[idName]\n\n        const req = store.put(object)\n        delete object[idName]\n\n        ids.push(id)\n        if (!id) {\n          req.onsuccess = () => {\n            const id = req.result as number\n            ids[i] = id\n            changeSet.registerChange(id, object)\n            if (i === objects.length - 1) {\n              resolve(ids as number[])\n            }\n          }\n        } else {\n          changeSet.registerChange(id, object)\n          if (i === objects.length - 1) {\n            req.onsuccess = () => {\n              resolve(ids as number[])\n            }\n          }\n        }\n        req.onerror = () => {\n          txn.abort()\n          reject(req.error)\n        }\n      }\n    })\n  }\n\n  private deleteLinks(txn: IsarTxn, keys: IDBValidKey[]): Promise<void> {\n    if (this.links.length === 0 && this.backlinkStoreNames.length === 0) {\n      return Promise.resolve()\n    }\n    const linkPromises = this.links.map(l => {\n      return bulkDelete(txn, l.storeName, keys.map(IsarLink.getLinkKeyRange))\n    })\n    const backlinkPromises = this.backlinkStoreNames.map(storeName => {\n      return bulkDeleteByIndex(txn, storeName, IsarLink.BacklinkIndex, keys)\n    })\n    return Promise.all([...linkPromises, ...backlinkPromises]).then(() => { })\n  }\n\n  deleteAll(txn: IsarTxn, ids: number[]): Promise<void> {\n    return bulkDelete(txn, this.name, ids).then(() => {\n      const changeSet = txn.getChangeSet(this.name)\n      for (let id of ids) {\n        changeSet.registerChange(id)\n      }\n      return this.deleteLinks(txn, ids)\n    })\n  }\n\n  deleteAllByIndex(\n    txn: IsarTxn,\n    indexName: string,\n    keys: IndexKey[],\n  ): Promise<number> {\n    return bulkDeleteByIndex(txn, this.name, indexName, keys).then(ids => {\n      const changeSet = txn.getChangeSet(this.name)\n      for (let id of ids as number[]) {\n        changeSet.registerChange(id)\n      }\n      return this.deleteLinks(txn, ids).then(() => ids.length)\n    })\n  }\n\n  clear(txn: IsarTxn): Promise<void> {\n    return new Promise((resolve, reject) => {\n      const storeNames = [\n        this.name,\n        ...this.backlinkStoreNames,\n        ...this.links.map(l => l.storeName),\n      ]\n      for (let i = 0; i < storeNames.length; i++) {\n        const store = txn.txn.objectStore(this.name)\n        const req = store.clear()\n        req.onerror = () => {\n          reject(req.error)\n        }\n        if (i === storeNames.length - 1) {\n          req.onsuccess = () => {\n            txn.getChangeSet(this.name).registerCleared()\n            resolve()\n          }\n        }\n      }\n    })\n  }\n}\n"
  },
  {
    "path": "packages/isar_web/src/cursor.ts",
    "content": "import { IsarTxn } from './txn'\n\ntype CursorParams = {\n  txn: IsarTxn\n  callback: CursorCallback\n  storeName: string\n  indexName?: string\n  range?: IDBKeyRange\n  offset?: number\n  direction?: IDBCursorDirection\n}\n\ntype CursorCallback = (\n  id: any,\n  value: any,\n  next: Function,\n  resolve: Function,\n  reject: Function,\n) => void\n\nexport function useCursor(params: CursorParams): Promise<void> {\n  return new Promise((resolve, reject) => {\n    const store = params.txn.txn.objectStore(params.storeName)\n    const source =\n      params.indexName != null ? store.index(params.indexName) : store\n    const multiEntry = params.indexName && (source as IDBIndex).multiEntry\n\n    const cursorReq = source.openCursor(params.range, params.direction)\n    cursorReq.onsuccess = () => {\n      const cursor = cursorReq.result\n      if (cursor) {\n        if (params.offset) {\n          cursor.advance(params.offset)\n          params.offset = undefined\n        } else {\n          if (multiEntry) {\n            const isArray = Array.isArray(\n              cursor.value[source.keyPath as string],\n            )\n            if (!isArray) {\n              cursor.continue()\n              return\n            }\n          }\n          params.callback(\n            cursor.primaryKey,\n            cursor.value,\n            function () {\n              cursor.continue()\n            },\n            resolve,\n            reject,\n          )\n        }\n      } else {\n        resolve()\n      }\n    }\n    cursorReq.onerror = e => {\n      reject(e)\n    }\n  })\n}\n"
  },
  {
    "path": "packages/isar_web/src/index.ts",
    "content": "import { IsarCollection } from './collection'\nimport { IsarInstance } from './instance'\nimport { IsarLink } from './link'\nimport { openIsar } from './open'\nimport { IsarQuery } from './query'\nimport { IsarTxn } from './txn'\n\n;(window as any).openIsar = openIsar\n;(window as any).IsarInstance = IsarInstance\n;(window as any).IsarTxn = IsarTxn\n;(window as any).IsarCollection = IsarCollection\n;(window as any).IsarQuery = IsarQuery\n;(window as any).IsarLink = IsarLink\n"
  },
  {
    "path": "packages/isar_web/src/instance.ts",
    "content": "import { IsarCollection } from './collection'\nimport { LinkSchema, Schema } from './schema'\nimport { IsarTxn } from './txn'\nimport { ChangeSet } from './watcher'\nimport { BroadcastChannel } from 'broadcast-channel'\n\nexport const idName = '_id';\n\nexport class IsarInstance {\n  private static readonly bc = new BroadcastChannel('ISAR_CHANNEL')\n\n  private readonly db: IDBDatabase\n  private readonly relaxedDurability: boolean\n  private collections: Map<string, IsarCollection<any>> = new Map()\n  private eventHandler: EventListener\n\n  constructor(db: IDBDatabase, relaxedDurability: boolean, schemas: Schema[]) {\n    this.db = db\n    this.relaxedDurability = relaxedDurability\n    this.initializeCollections(schemas)\n\n    this.eventHandler = (event: MessageEvent) => {\n      if (\n        event.data &&\n        event.data.type === 'change' &&\n        event.data.instance == this.db.name\n      ) {\n        this.notifyWatchers(event.data.changes, true)\n      }\n    }\n    IsarInstance.bc.addEventListener('message', this.eventHandler)\n  }\n\n  private initializeCollections(schemas: Schema[]) {\n    for (let schema of schemas) {\n      const backlinkStoreNames = schemas.flatMap(s => {\n        if (s.name === schema.name) {\n          return []\n        }\n        return s.links\n          .filter(l => l.target === schema.name)\n          .map(l => {\n            return LinkSchema.getStoreName(s.name, l.target, l.name)\n          })\n      })\n      const col = new IsarCollection(this, schema, backlinkStoreNames)\n      this.collections.set(schema.name, col)\n    }\n  }\n\n  notifyWatchers(\n    changes: Map<string, ChangeSet<any>>,\n    external: boolean = false,\n  ) {\n    let txn: IsarTxn | undefined\n\n    const getTxn = () => {\n      if (txn == null) {\n        txn = this.beginTxn(false)\n      }\n      return txn!\n    }\n    for (let [colName, changeSet] of changes.entries()) {\n      const collection = this.getCollection(colName)\n      collection.notify(changeSet, getTxn)\n    }\n\n    if (!external) {\n      const event: ChangeEvent = {\n        type: 'change',\n        instance: this.db.name,\n        changes,\n      }\n      IsarInstance.bc.postMessage(event)\n    }\n  }\n\n  beginTxn(write: boolean): IsarTxn {\n    const names = this.db.objectStoreNames\n    const mode = write ? 'readwrite' : 'readonly'\n    const options = this.relaxedDurability ? { durability: 'relaxed' } : {}\n    const txn = (this.db as any).transaction(names, mode, options)\n    return new IsarTxn(this, txn, write)\n  }\n\n  getCollection<OBJ>(name: string): IsarCollection<OBJ> {\n    return this.collections.get(name)!\n  }\n\n  close(deleteFromDisk: boolean = false): Promise<void> {\n    IsarInstance.bc.removeEventListener('message', this.eventHandler)\n    this.db.close()\n    if (deleteFromDisk) {\n      const req = indexedDB.deleteDatabase(this.db.name)\n      return new Promise((resolve, reject) => {\n        req.onsuccess = () => {\n          resolve()\n        }\n        req.onerror = () => {\n          reject(req.error)\n        }\n      })\n    } else {\n      return Promise.resolve()\n    }\n  }\n}\n\ntype ChangeEvent = {\n  type: 'change'\n  instance: string\n  changes: Map<string, ChangeSet<any>>\n}\n"
  },
  {
    "path": "packages/isar_web/src/link.ts",
    "content": "import { bulkDelete } from './bulk-delete'\nimport { IsarInstance } from './instance'\nimport { LinkSchema } from './schema'\nimport { IsarTxn } from './txn'\n\nexport class IsarLink {\n  static readonly BacklinkIndex = 'backlink'\n\n  readonly isar: IsarInstance\n  readonly name: string\n  readonly sourceName: string\n  readonly targetName: string\n  readonly storeName: string\n\n  constructor(\n    isar: IsarInstance,\n    name: string,\n    sourceName: string,\n    targetName: string,\n  ) {\n    this.isar = isar\n    this.name = name\n    this.sourceName = sourceName\n    this.targetName = targetName\n    this.storeName = LinkSchema.getStoreName(sourceName, targetName, name)\n  }\n\n  private getLinkEntry(source: number, target: number, backlink: boolean): any {\n    if (backlink) {\n      ;[source, target] = [target, source]\n    }\n    return {\n      a: source,\n      b: target,\n    }\n  }\n\n  static getLinkKeyRange(id: number): IDBKeyRange {\n    return IDBKeyRange.bound([id, -Infinity], [id, Infinity])\n  }\n\n  update(\n    txn: IsarTxn,\n    backlink: boolean,\n    id: number,\n    addedTargets: number[],\n    deletedTargets: number[],\n  ): Promise<void> {\n    if (addedTargets.length === 0 && deletedTargets.length === 0) {\n      return Promise.resolve()\n    }\n\n    return new Promise((resolve, reject) => {\n      const store = txn.txn.objectStore(this.storeName)\n\n      const deletedEmpty = deletedTargets.length === 0\n      for (let i = 0; i < addedTargets.length; i++) {\n        let target = addedTargets[i]\n        const req = store.add(this.getLinkEntry(id, target, backlink))\n        if (deletedEmpty && i === addedTargets.length - 1) {\n          req.onsuccess = () => {\n            resolve()\n          }\n        }\n        req.onerror = () => {\n          txn.abort()\n          reject(req.error)\n        }\n      }\n\n      for (let i = 0; i < deletedTargets.length; i++) {\n        let target = deletedTargets[i]\n        const key = backlink ? [target, id] : [id, target]\n        const req = store.delete(key)\n        if (i === deletedTargets.length - 1) {\n          req.onsuccess = () => {\n            resolve()\n          }\n        }\n        req.onerror = () => {\n          txn.abort()\n          reject(req.error)\n        }\n      }\n    })\n  }\n\n  clear(txn: IsarTxn, id: number, backlink: boolean): Promise<void> {\n    return new Promise((resolve, reject) => {\n      const store = txn.txn.objectStore(this.storeName)\n      if (backlink) {\n        const keysRes = store.index(IsarLink.BacklinkIndex).getAllKeys(id)\n        keysRes.onsuccess = () => {\n          const keys = keysRes.result\n          if (keys.length > 0) {\n            const ids = keys.map(key => (key as number[])[1])\n            bulkDelete(txn, this.storeName, ids).then(resolve, reject)\n          } else {\n            resolve()\n          }\n        }\n        keysRes.onerror = () => {\n          txn.abort()\n          reject(keysRes.error)\n        }\n      } else {\n        const deleteReq = store.delete(IsarLink.getLinkKeyRange(id))\n        deleteReq.onsuccess = () => {\n          resolve()\n        }\n        deleteReq.onerror = () => {\n          txn.abort()\n          reject(deleteReq.error)\n        }\n      }\n    })\n  }\n}\n"
  },
  {
    "path": "packages/isar_web/src/open.ts",
    "content": "import equal from 'fast-deep-equal'\nimport { IsarInstance } from './instance'\nimport { IsarLink } from './link'\nimport { IndexSchema, IsarType, LinkSchema, Schema } from './schema'\n\n// Polyfill for older browsers\nif (typeof IDBTransaction.prototype.commit !== \"function\") {\n  IDBTransaction.prototype.commit = function () { }\n}\n\nexport function openIsar(\n  name: string,\n  schemas: Schema[],\n  relaxedDurability: boolean,\n): Promise<IsarInstance> {\n  return openInternal(name, schemas, relaxedDurability)\n}\n\nfunction openInternal(\n  name: string,\n  schemas: Schema[],\n  relaxedDurability: boolean,\n  version?: number,\n): Promise<IsarInstance> {\n  return new Promise((resolve, reject) => {\n    const req = indexedDB.open(name, version)\n    req.onsuccess = () => {\n      const db = req.result\n      if (version == null) {\n        const txn = db.transaction(db.objectStoreNames, 'readonly')\n        if (!performUpgrade(txn, true, schemas)) {\n          const newVersion = txn.db.version + 1\n          db.close()\n          resolve(openInternal(name, schemas, relaxedDurability, newVersion))\n          return\n        }\n      }\n\n      const instance = new IsarInstance(db, relaxedDurability, schemas)\n      resolve(instance)\n    }\n    req.onupgradeneeded = () => {\n      performUpgrade(req.transaction!, false, schemas)\n    }\n    req.onerror = () => {\n      reject(req.error)\n    }\n  })\n}\n\nfunction performUpgrade(\n  txn: IDBTransaction,\n  dryRun: boolean,\n  schemas: Schema[],\n): boolean {\n  const schemaStoreNames: string[] = []\n  for (const schema of schemas) {\n    schemaStoreNames.push(schema.name)\n    const schemaIndexNames: string[] = []\n\n    let store: IDBObjectStore\n    if (!txn.objectStoreNames.contains(schema.name)) {\n      if (dryRun) {\n        return false\n      }\n      store = txn.db.createObjectStore(schema.name, {\n        autoIncrement: true,\n      })\n    } else {\n      store = txn.objectStore(schema.name)\n    }\n\n    for (let indexSchema of schema.indexes) {\n      schemaIndexNames.push(indexSchema.name)\n      if (store.indexNames.contains(indexSchema.name)) {\n        const index = store.index(indexSchema.name)\n        if (IndexSchema.matchesIndex(schema, indexSchema, index)) {\n          continue\n        } else {\n          if (!dryRun) {\n            store.deleteIndex(indexSchema.name)\n          }\n        }\n      }\n      if (dryRun) {\n        return false\n      }\n      store.createIndex(indexSchema.name, IndexSchema.getKeyPath(indexSchema), {\n        unique: indexSchema.unique,\n        multiEntry: IndexSchema.isIndexMultiEntry(schema, indexSchema),\n      })\n    }\n\n    for (let linkSchema of schema.links) {\n      const name = LinkSchema.getStoreName(\n        schema.name,\n        linkSchema.target,\n        linkSchema.name,\n      )\n      let linkStore: IDBObjectStore\n      if (!txn.objectStoreNames.contains(name)) {\n        if (dryRun) {\n          return false\n        }\n        linkStore = txn.db.createObjectStore(name, {\n          keyPath: ['a', 'b'],\n          autoIncrement: false,\n        })\n      } else {\n        linkStore = txn.objectStore(name)\n      }\n      schemaStoreNames.push(name)\n\n      const indexesOk = equal(\n        [...linkStore.indexNames],\n        [IsarLink.BacklinkIndex],\n      )\n      if (!indexesOk) {\n        if (dryRun) {\n          return false\n        }\n        for (let indexName of linkStore.indexNames) {\n          linkStore.deleteIndex(indexName)\n        }\n        linkStore.createIndex(IsarLink.BacklinkIndex, 'b')\n      }\n    }\n\n    for (let indexName of store.indexNames) {\n      if (schemaIndexNames.indexOf(indexName) === -1) {\n        if (dryRun) {\n          return false\n        }\n        store.deleteIndex(indexName)\n      }\n    }\n  }\n\n  for (let storeName of txn.objectStoreNames) {\n    if (schemaStoreNames.indexOf(storeName) === -1) {\n      if (dryRun) {\n        return false\n      }\n      txn.db.deleteObjectStore(storeName)\n    }\n  }\n\n  return true\n}\n\ntype CollectionInfo = {\n  properties: {\n    [key: string]: CollectionProperty\n  }\n  nextId: number\n}\n\ntype CollectionProperty = {\n  id: number\n  type: IsarType\n}"
  },
  {
    "path": "packages/isar_web/src/query.ts",
    "content": "import { IsarCollection } from './collection'\nimport { useCursor } from './cursor'\nimport { idName } from './instance'\nimport { IsarLink } from './link'\nimport { IsarTxn } from './txn'\n\ntype IdWherClause = {\n  range?: IDBKeyRange\n}\n\ntype IndexWherClause = {\n  indexName: string\n  range?: IDBKeyRange\n}\n\ntype LinkWherClause = {\n  linkCollection: string\n  linkName: string\n  backlink: boolean\n  id: number\n}\n\ntype WherClause = IdWherClause | IndexWherClause | LinkWherClause\n\ntype Filter = (id: number, obj: any) => boolean\n\ntype Cmp = (a: any, b: any) => number\n\ntype DistinctValue = (obj: any) => string\n\nexport class IsarQuery<OBJ> {\n  collection: IsarCollection<OBJ>\n  whereClauses: WherClause[]\n  whereClauseDirection: IDBCursorDirection\n  filter?: Filter\n  sortCmp?: Cmp\n  distinctValue?: DistinctValue\n  offset: number\n  limit: number\n\n  constructor(\n    collection: IsarCollection<OBJ>,\n    whereClauses: WherClause[],\n    whereDistinct: boolean,\n    whereAscending: boolean,\n    filter?: Filter,\n    sortCmp?: Cmp,\n    distinctValue?: DistinctValue,\n    offset?: number,\n    limit?: number,\n  ) {\n    this.collection = collection\n    this.whereClauses = whereClauses\n    this.filter = filter\n    this.sortCmp = sortCmp\n    this.distinctValue = distinctValue\n    this.offset = offset ?? 0\n    this.limit = limit ?? Infinity\n\n    if (whereDistinct) {\n      this.whereClauseDirection = whereAscending ? 'nextunique' : 'prevunique'\n    } else {\n      this.whereClauseDirection = whereAscending ? 'next' : 'prev'\n    }\n\n    if (this.whereClauses.length === 0) {\n      this.whereClauses.push({})\n    }\n  }\n\n  private getWhereClauseRange(\n    whereClause: IdWherClause | IndexWherClause,\n  ): IDBKeyRange {\n    return whereClause.range ?? IDBKeyRange.lowerBound(-Infinity)\n  }\n\n  private async findInternal(txn: IsarTxn, limit: number): Promise<any[]> {\n    const offset = this.offset\n    const unsortedLimit = !this.sortCmp ? offset + limit : Infinity\n    const unsortedDistinct = !this.sortCmp ? this.distinctValue : undefined\n    let results: OBJ[] = []\n    const idsSet = new Set<number>()\n    const distinctSet = new Set<String>()\n\n    const cursorCallback = (\n      id: any,\n      object: any,\n      next: Function,\n      resolve: Function,\n    ) => {\n      if (idsSet.has(id)) {\n        next()\n        return\n      } else {\n        idsSet.add(id)\n      }\n\n      if (this.filter) {\n        if (!this.filter(id, object)) {\n          next()\n          return\n        }\n      }\n      if (unsortedDistinct) {\n        const value = unsortedDistinct(object)\n        if (distinctSet.has(value)) {\n          next()\n          return\n        } else {\n          distinctSet.add(value)\n        }\n      }\n      object[idName] = id\n      results.push(object)\n      if (results.length < unsortedLimit) {\n        next()\n      } else {\n        resolve()\n      }\n    }\n\n    for (const whereClause of this.whereClauses) {\n      if (results.length >= unsortedLimit) {\n        break\n      }\n      if ('linkName' in whereClause) {\n        const link = this.collection.isar\n          .getCollection(whereClause.linkCollection)\n          .getLink(whereClause.linkName)!\n        await useCursor({\n          txn,\n          storeName: link.storeName,\n          indexName: whereClause.backlink ? IsarLink.BacklinkIndex : undefined,\n          range: IsarLink.getLinkKeyRange(whereClause.id),\n          direction: this.whereClauseDirection,\n          callback: (key, _, next, resolve, reject) => {\n            const id = (key as number[])[whereClause.backlink ? 0 : 1]\n            this.collection\n              .get(txn, id)\n              .then(obj => {\n                if (obj) {\n                  cursorCallback(id, obj, next, resolve)\n                } else {\n                  next()\n                }\n              })\n              .catch(() => reject())\n          },\n        })\n      } else {\n        const range = this.getWhereClauseRange(whereClause)\n        await useCursor({\n          txn,\n          storeName: this.collection.name,\n          indexName:\n            'indexName' in whereClause ? whereClause.indexName : undefined,\n          range: range,\n          direction: this.whereClauseDirection,\n          callback: cursorCallback,\n        })\n      }\n    }\n\n    if (this.sortCmp) {\n      results.sort(this.sortCmp)\n\n      const distinctValue = this.distinctValue\n      if (distinctValue) {\n        results = results.filter(obj => {\n          const value = distinctValue!(obj)\n          if (!distinctSet.has(value)) {\n            distinctSet.add(value)\n            return true\n          } else {\n            return false\n          }\n        })\n      }\n    }\n\n    return results.slice(offset, offset + limit)\n  }\n\n  findFirst(txn: IsarTxn): Promise<OBJ | undefined> {\n    return this.findInternal(txn, 1).then(results => {\n      return results.length > 0 ? results[0] : undefined\n    })\n  }\n\n  findAll(txn: IsarTxn): Promise<OBJ[]> {\n    return this.findInternal(txn, this.limit ?? Infinity)\n  }\n\n  deleteFirst(txn: IsarTxn): Promise<boolean> {\n    return this.findInternal(txn, 1).then(result => {\n      if (result.length !== 0) {\n        return this.collection\n          .deleteAll(txn, [result[0][idName]])\n          .then(() => true)\n      } else {\n        return false\n      }\n    })\n  }\n\n  deleteAll(txn: IsarTxn): Promise<number> {\n    return this.findInternal(txn, this.limit).then(result => {\n      return this.collection\n        .deleteAll(txn, result.map(obj => obj[idName]))\n        .then(() => result.length)\n    })\n  }\n\n  min(txn: IsarTxn, key: string): Promise<number | undefined> {\n    return this.findAll(txn).then(results => {\n      let min: number | undefined = undefined\n      for (const obj of results) {\n        const value = (obj as any)[key]\n        if (value != null && (min == null || value < min)) {\n          min = value\n        }\n      }\n      return min\n    })\n  }\n\n  max(txn: IsarTxn, key: string): Promise<number | undefined> {\n    return this.findAll(txn).then(results => {\n      let max: number | undefined = undefined\n      for (const obj of results) {\n        const value = (obj as any)[key]\n        if (value != null && (max == null || value > max)) {\n          max = value\n        }\n      }\n      return max\n    })\n  }\n\n  sum(txn: IsarTxn, key: string): Promise<number> {\n    return this.findAll(txn).then(results => {\n      let sum = 0\n      for (const obj of results) {\n        const value = (obj as any)[key]\n        if (value != null) {\n          sum += value\n        }\n      }\n      return sum\n    })\n  }\n\n  average(txn: IsarTxn, key: string): Promise<number> {\n    return this.findAll(txn).then(results => {\n      let sum = 0\n      let count = 0\n      for (const obj of results) {\n        const value = (obj as any)[key]\n        if (value != null) {\n          sum += value\n          count++\n        }\n      }\n      return sum / count\n    })\n  }\n\n  count(txn: IsarTxn): Promise<number> {\n    return this.findAll(txn).then(result => result.length)\n  }\n\n  private whereClauseMatches(id: number, object: OBJ) {\n    for (const whereClause of this.whereClauses) {\n      if ('linkName' in whereClause) {\n        return true\n      } else if ('indexName' in whereClause) {\n        if (this.collection.isMultiEntryIndex(whereClause.indexName)) {\n          const values = (object as any)[\n            this.collection.getIndexKeyPath(whereClause.indexName!)[0]\n          ]\n          for (let value of values) {\n            if (this.getWhereClauseRange(whereClause).includes(value)) {\n              return true\n            }\n          }\n        } else {\n          let value = this.collection\n            .getIndexKeyPath(whereClause.indexName!)\n            .map(p =>\n              p === this.collection.idName ? id : (object as any)[p],\n            )\n          if (value.length === 1) {\n            value = value[0]\n          }\n          if (this.getWhereClauseRange(whereClause).includes(value)) {\n            return true\n          }\n        }\n      } else if (this.getWhereClauseRange(whereClause).includes(id)) {\n        return true\n      }\n    }\n\n    return false\n  }\n\n  whereClauseAndFilterMatch(id: number, idbObject: OBJ): boolean {\n    if (!this.whereClauseMatches(id, idbObject)) {\n      return false\n    }\n\n    if (this.filter) {\n      if (!this.filter(id, idbObject)) {\n        return false\n      }\n    }\n\n    return true\n  }\n}\n"
  },
  {
    "path": "packages/isar_web/src/schema.ts",
    "content": "import equal from 'fast-deep-equal'\n\nexport type Schema = {\n  name: string\n  properties: Array<PropertySchema>\n  indexes: Array<IndexSchema>\n  links: Array<LinkSchema>\n}\n\ntype PropertySchema = {\n  name: string\n  type: IsarType\n}\n\ntype IndexSchema = {\n  name: string\n  unique: boolean\n  properties: Array<IndexPropertySchema>\n}\n\ntype IndexPropertySchema = {\n  name: string\n  type: IndexType\n  caseSensitive: boolean\n}\n\nexport namespace IndexSchema {\n  export function isIndexMultiEntry(\n    schema: Schema,\n    indexSchema: IndexSchema,\n  ): boolean {\n    return indexSchema.properties.some(ip => {\n      const property = schema.properties.find(p => p.name === ip.name)!\n      return ip.type === IndexType.Value && IsarType.isList(property.type)\n    })\n  }\n\n  export function getKeyPath(indexSchema: IndexSchema): string | string[] {\n    return indexSchema.properties.length === 1\n      ? indexSchema.properties[0].name\n      : indexSchema.properties.map(p => p.name)\n  }\n\n  export function matchesIndex(\n    schema: Schema,\n    indexSchema: IndexSchema,\n    index: IDBIndex,\n  ): boolean {\n    return (\n      index.name === indexSchema.name &&\n      index.multiEntry === isIndexMultiEntry(schema, indexSchema) &&\n      index.unique === indexSchema.unique &&\n      equal(index.keyPath, getKeyPath(indexSchema))\n    )\n  }\n}\n\ntype LinkSchema = {\n  name: string\n  target: string\n}\n\nexport namespace LinkSchema {\n  export function getStoreName(\n    sourceName: string,\n    targetName: string,\n    linkName: string,\n  ): string {\n    return `_${sourceName}_${targetName}_${linkName}`\n  }\n}\n\nexport enum IsarType {\n  Bool = 'Bool',\n  Byte = 'Byte',\n  Int = 'Int',\n  Float = 'Float',\n  Long = 'Long',\n  Double = 'Double',\n  DateTime = 'DateTime',\n  String = 'String',\n  Object = 'Object',\n  BoolList = 'BoolList',\n  ByteList = 'ByteList',\n  IntList = 'IntList',\n  FloatList = 'FloatList',\n  LongList = 'LongList',\n  DoubleList = 'DoubleList',\n  DateTimeList = 'DateTimeList',\n  StringList = 'StringList',\n  ObjectList = 'ObjectList',\n}\n\nexport namespace IsarType {\n  export function isList(type: IsarType): boolean {\n    return type.endsWith('List')\n  }\n}\n\nenum IndexType {\n  Value = 'Value',\n  Hash = 'Hash',\n  HashElements = 'HashElements',\n}\n"
  },
  {
    "path": "packages/isar_web/src/txn.ts",
    "content": "import { IsarInstance } from './instance'\nimport { IsarChangeSet } from './watcher'\n\nexport class IsarTxn {\n  readonly isar: IsarInstance\n  readonly txn: IDBTransaction\n  active: boolean\n  readonly write: boolean\n  private readonly changes: Map<string, IsarChangeSet<any>> | undefined\n\n  constructor(isar: IsarInstance, txn: IDBTransaction, write: boolean) {\n    this.isar = isar\n    this.txn = txn\n    this.active = true\n    this.write = write\n\n    if (write) {\n      this.changes = new Map()\n    }\n  }\n\n  getChangeSet<OBJ>(collectionName: string): IsarChangeSet<OBJ> {\n    let changeSet = this.changes!.get(collectionName)\n    if (changeSet == null) {\n      changeSet = new IsarChangeSet()\n      this.changes!.set(collectionName, changeSet)\n    }\n    return changeSet\n  }\n\n  commit(): Promise<void> {\n    return new Promise((resolve, reject) => {\n      this.active = false\n\n      this.txn.oncomplete = () => {\n        if (this.changes) {\n          this.isar.notifyWatchers(this.changes)\n        }\n        resolve()\n      }\n      this.txn.onerror = () => {\n        reject(this.txn.error)\n      }\n      this.txn.commit()\n    })\n  }\n\n  abort() {\n    if (this.active) {\n      this.active = false\n      this.txn.abort()\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_web/src/watcher.ts",
    "content": "import { IsarQuery } from './query'\nimport { IsarTxn } from './txn'\n\ntype ChangeCallback = () => void\n\ntype ObjectChangeCallback<OBJ> = (object?: OBJ) => void\n\ntype QueryChangeCallback<OBJ> = (results: OBJ[]) => void\n\ntype StopWatching = () => void\n\ntype QueryWatcher<OBJ> = {\n  callback: ChangeCallback | QueryChangeCallback<OBJ>\n  query: IsarQuery<OBJ>\n  lazy: boolean\n}\n\nexport type ChangeSet<OBJ> = {\n  cleared: boolean\n  addedObjects: Map<number, OBJ>\n  deletedObjectIds: Set<number>\n}\n\nexport class IsarChangeSet<OBJ> implements IsarChangeSet<OBJ> {\n  cleared: boolean = false\n  addedObjects: Map<number, OBJ> = new Map()\n  deletedObjectIds: Set<number> = new Set()\n\n  registerChange(id: number, idbObject?: OBJ) {\n    if (idbObject) {\n      this.addedObjects.set(id, idbObject)\n      this.deletedObjectIds.delete(id)\n    } else {\n      this.deletedObjectIds.add(id)\n      this.addedObjects.delete(id)\n    }\n  }\n\n  registerCleared() {\n    this.addedObjects.clear()\n    this.deletedObjectIds.clear()\n    this.cleared = true\n  }\n}\n\nexport class IsarWatchable<OBJ> {\n  readonly collectionWatchers = new Set<ChangeCallback>()\n  readonly objectWatchers: Map<number, Set<ObjectChangeCallback<OBJ>>> =\n    new Map()\n  readonly queryWatchers = new Set<QueryWatcher<OBJ>>()\n\n  watchLazy(callback: ChangeCallback): StopWatching {\n    this.collectionWatchers.add(callback)\n    return () => this.collectionWatchers.delete(callback)\n  }\n\n  watchObject(id: number, callback: ObjectChangeCallback<OBJ>): StopWatching {\n    let ow = this.objectWatchers.get(id)\n    if (ow == null) {\n      ow = new Set()\n      this.objectWatchers.set(id, ow)\n    }\n    ow.add(callback)\n    return () => {\n      if (ow!.size <= 1) {\n        this.objectWatchers.delete(id)\n      } else {\n        ow!.delete(callback)\n      }\n    }\n  }\n\n  private watchQueryInternal(\n    query: IsarQuery<OBJ>,\n    lazy: boolean,\n    callback: ChangeCallback | QueryChangeCallback<OBJ>,\n  ): StopWatching {\n    const watcher = { callback, query, lazy }\n    this.queryWatchers.add(watcher)\n    return () => this.queryWatchers.delete(watcher)\n  }\n\n  watchQuery(\n    query: IsarQuery<OBJ>,\n    callback: QueryChangeCallback<OBJ>,\n  ): StopWatching {\n    return this.watchQueryInternal(query, false, callback)\n  }\n\n  watchQueryLazy(\n    query: IsarQuery<OBJ>,\n    callback: ChangeCallback,\n  ): StopWatching {\n    return this.watchQueryInternal(query, true, callback)\n  }\n\n  notify(changes: ChangeSet<OBJ>, getTxn: () => IsarTxn) {\n    if (\n      !changes.cleared &&\n      changes.addedObjects.size === 0 &&\n      changes.deletedObjectIds.size === 0\n    ) {\n      return\n    }\n\n    function notifyQuery(watcher: QueryWatcher<OBJ>) {\n      if (watcher.lazy) {\n        ;(watcher.callback as ChangeCallback)()\n      } else {\n        const txn = getTxn()\n        watcher.query.findAll(txn).then(watcher.callback)\n      }\n    }\n\n    for (const watcher of this.collectionWatchers) {\n      watcher()\n    }\n\n    let queryWatchers: Set<QueryWatcher<OBJ>> | undefined\n    if (changes.cleared || changes.deletedObjectIds.size > 0) {\n      for (const watcher of this.queryWatchers) {\n        notifyQuery(watcher)\n      }\n    } else {\n      queryWatchers = new Set(this.queryWatchers)\n    }\n\n    if (changes.cleared) {\n      for (const [id, callbacks] of this.objectWatchers) {\n        for (let callback of callbacks) {\n          callback(changes.addedObjects.get(id))\n        }\n      }\n    } else {\n      for (const id of changes.deletedObjectIds) {\n        const callbacks = this.objectWatchers.get(id)\n        if (callbacks != null) {\n          for (let callback of callbacks) {\n            callback(undefined)\n          }\n        }\n      }\n\n      for (const [id, added] of changes.addedObjects) {\n        const ow = this.objectWatchers.get(id)\n        if (ow != null) {\n          for (let callback of ow) {\n            callback(added)\n          }\n        }\n\n        if (queryWatchers != null) {\n          for (const watcher of queryWatchers) {\n            if (watcher.query.whereClauseAndFilterMatch(id, added)) {\n              notifyQuery(watcher)\n              queryWatchers.delete(watcher)\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/isar_web/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/\",\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"module\": \"es6\",\n    \"target\": \"es2017\",\n    \"jsx\": \"react\",\n    \"allowJs\": true,\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n  }\n}"
  },
  {
    "path": "packages/isar_web/webpack.config.js",
    "content": "const path = require('path')\n\nmodule.exports = {\n  entry: './src/index.ts',\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        use: 'ts-loader',\n        exclude: /node_modules/,\n      },\n    ],\n  },\n  resolve: {\n    extensions: ['.tsx', '.ts', '.js'],\n  },\n  output: {\n    filename: 'index.js',\n    path: path.resolve(__dirname, 'dist'),\n  },\n  devtool: 'source-map',\n}\n"
  },
  {
    "path": "packages/mdbx_sys/.gitignore",
    "content": "libmdbx/"
  },
  {
    "path": "packages/mdbx_sys/Cargo.toml",
    "content": "[package]\nname = \"mdbx-sys\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n[lib]\nname = \"mdbx_sys\"\n\n[dependencies]\nlibc = \"0.2\"\n\n[build-dependencies]\ncmake = \"0.1\"\ncc = \"1.0\"\nbindgen = { version = \"0.63\", default-features = false, features = [\"runtime\"] }\n"
  },
  {
    "path": "packages/mdbx_sys/build.rs",
    "content": "use bindgen::callbacks::{IntKind, ParseCallbacks};\nuse std::process::Command;\nuse std::{env, fs, path::PathBuf};\n\n#[derive(Debug)]\nstruct Callbacks;\n\nimpl ParseCallbacks for Callbacks {\n    fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {\n        match name {\n            \"MDBX_SUCCESS\"\n            | \"MDBX_KEYEXIST\"\n            | \"MDBX_NOTFOUND\"\n            | \"MDBX_PAGE_NOTFOUND\"\n            | \"MDBX_CORRUPTED\"\n            | \"MDBX_PANIC\"\n            | \"MDBX_VERSION_MISMATCH\"\n            | \"MDBX_INVALID\"\n            | \"MDBX_MAP_FULL\"\n            | \"MDBX_DBS_FULL\"\n            | \"MDBX_READERS_FULL\"\n            | \"MDBX_TLS_FULL\"\n            | \"MDBX_TXN_FULL\"\n            | \"MDBX_CURSOR_FULL\"\n            | \"MDBX_PAGE_FULL\"\n            | \"MDBX_MAP_RESIZED\"\n            | \"MDBX_INCOMPATIBLE\"\n            | \"MDBX_BAD_RSLOT\"\n            | \"MDBX_BAD_TXN\"\n            | \"MDBX_BAD_VALSIZE\"\n            | \"MDBX_BAD_DBI\"\n            | \"MDBX_LOG_DONTCHANGE\"\n            | \"MDBX_DBG_DONTCHANGE\"\n            | \"MDBX_RESULT_TRUE\"\n            | \"MDBX_UNABLE_EXTEND_MAPSIZE\"\n            | \"MDBX_PROBLEM\"\n            | \"MDBX_LAST_LMDB_ERRCODE\"\n            | \"MDBX_BUSY\"\n            | \"MDBX_EMULTIVAL\"\n            | \"MDBX_EBADSIGN\"\n            | \"MDBX_WANNA_RECOVERY\"\n            | \"MDBX_EKEYMISMATCH\"\n            | \"MDBX_TOO_LARGE\"\n            | \"MDBX_THREAD_MISMATCH\"\n            | \"MDBX_TXN_OVERLAPPING\"\n            | \"MDBX_LAST_ERRCODE\" => Some(IntKind::Int),\n            _ => Some(IntKind::UInt),\n        }\n    }\n}\n\nconst LIBMDBX_REPO: &str = \"https://github.com/isar/libmdbx.git\";\nconst LIBMDBX_TAG: &str = \"v0.12.9\";\n\nfn main() {\n    println!(\"cargo:rerun-if-changed=build.rs\");\n    env::set_var(\"IPHONEOS_DEPLOYMENT_TARGET\", \"12.0\");\n\n    let is_android = env::var(\"CARGO_CFG_TARGET_OS\").unwrap() == \"android\";\n\n    let _ = fs::remove_dir_all(\"libmdbx\");\n\n    Command::new(\"git\")\n        .arg(\"clone\")\n        .arg(LIBMDBX_REPO)\n        .arg(\"--branch\")\n        .arg(LIBMDBX_TAG)\n        .output()\n        .unwrap();\n\n    Command::new(\"make\")\n        .arg(\"release-assets\")\n        .current_dir(\"libmdbx\")\n        .output()\n        .unwrap();\n\n    let mut mdbx = PathBuf::from(&env::var(\"CARGO_MANIFEST_DIR\").unwrap());\n    mdbx.push(\"libmdbx\");\n    mdbx.push(\"dist\");\n\n    let core_path = mdbx.join(\"mdbx.c\");\n    let mut core = fs::read_to_string(core_path.as_path()).unwrap();\n    core = core.replace(\"!CharToOemBuffA(buf, buf, size)\", \"false\");\n    if is_android {\n        core = core.replace(\n            \"memset(ior, -1, sizeof(osal_ioring_t))\",\n            \"memset(ior, 0, sizeof(osal_ioring_t))\",\n        );\n        core = core.replace(\"unlikely(linux_kernel_version < 0x04000000)\", \"false\");\n        core = core.replace(\n            \"assert(linux_kernel_version >= 0x03060000);\",\n            \"if (linux_kernel_version >= 0x03060000) return MDBX_SUCCESS;\n            __fallthrough\",\n        );\n    }\n    fs::write(core_path.as_path(), core).unwrap();\n\n    let out_path = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n\n    let bindings = bindgen::Builder::default()\n        .header(mdbx.join(\"mdbx.h\").to_string_lossy())\n        .allowlist_var(\"^(MDBX|mdbx)_.*\")\n        .allowlist_type(\"^(MDBX|mdbx)_.*\")\n        .allowlist_function(\"^(MDBX|mdbx)_.*\")\n        .rustified_enum(\"^(MDBX_option_t|MDBX_cursor_op)\")\n        .size_t_is_usize(false)\n        .ctypes_prefix(\"::libc\")\n        .parse_callbacks(Box::new(Callbacks))\n        .layout_tests(false)\n        .prepend_enum_name(false)\n        .generate_comments(true)\n        .disable_header_comment()\n        .rustfmt_bindings(true)\n        .generate()\n        .expect(\"Unable to generate bindings\");\n\n    bindings\n        .write_to_file(out_path.join(\"bindings.rs\"))\n        .expect(\"Couldn't write bindings!\");\n\n    let mut cc_builder = cc::Build::new();\n    let flags = format!(\"{:?}\", cc_builder.get_compiler().cflags_env());\n    cc_builder.flag_if_supported(\"-Wno-everything\");\n\n    if cfg!(windows) {\n        let dst = cmake::Config::new(&mdbx)\n            .define(\"MDBX_INSTALL_STATIC\", \"1\")\n            .define(\"MDBX_BUILD_CXX\", \"0\")\n            .define(\"MDBX_BUILD_TOOLS\", \"0\")\n            .define(\"MDBX_BUILD_SHARED_LIBRARY\", \"0\")\n            .define(\"MDBX_LOCK_SUFFIX\", \"\\\".lock\\\"\")\n            .define(\"MDBX_TXN_CHECKOWNER\", \"0\")\n            .define(\"MDBX_WITHOUT_MSVC_CRT\", \"1\")\n            // Setting HAVE_LIBM=1 is necessary to override issues with `pow` detection on Windows\n            .define(\"UNICODE\", \"1\")\n            .define(\"HAVE_LIBM\", \"1\")\n            .define(\"NDEBUG\", \"1\")\n            .cflag(\"/w\")\n            .init_c_cfg(cc_builder)\n            .build();\n\n        println!(\"cargo:rustc-link-lib=mdbx\");\n        println!(\n            \"cargo:rustc-link-search=native={}\",\n            dst.join(\"lib\").display()\n        );\n        println!(r\"cargo:rustc-link-lib=ntdll\");\n        println!(r\"cargo:rustc-link-search=C:\\windows\\system32\");\n    } else {\n        cc_builder\n            .define(\"MDBX_BUILD_FLAGS\", flags.as_str())\n            .define(\"MDBX_BUILD_CXX\", \"0\")\n            .define(\"MDBX_BUILD_TOOLS\", \"0\")\n            .define(\"MDBX_BUILD_SHARED_LIBRARY\", \"0\")\n            .define(\"MDBX_LOCK_SUFFIX\", \"\\\".lock\\\"\")\n            .define(\"MDBX_TXN_CHECKOWNER\", \"0\")\n            .define(\"MDBX_OSX_SPEED_INSTEADOF_DURABILITY\", \"1\")\n            .define(\"MDBX_HAVE_BUILTIN_CPU_SUPPORTS\", \"0\")\n            .define(\"NDEBUG\", \"1\")\n            .file(mdbx.join(\"mdbx.c\"))\n            .compile(\"libmdbx.a\");\n    }\n}\n"
  },
  {
    "path": "packages/mdbx_sys/src/lib.rs",
    "content": "#![deny(warnings)]\n#![allow(non_upper_case_globals)]\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(clippy::all)]\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/bindings.rs\"));\n"
  },
  {
    "path": "tool/build.sh",
    "content": "#!/bin/shuname\n\narch=$(uname -m)\n\nif [ `uname` = \"Linux\" ] ;\nthen\n    if [ $arch = \"x86_64\" ] ;\n    then\n        cargo build --target x86_64-unknown-linux-gnu --release\n    else\n        cargo build --target aarch64-unknown-linux-gnu --release\n    fi\nelif [ `uname` = \"Darwin\" ] ;\nthen\n     if [[ $arch == x86_64* ]]; then\n        cargo build --target x86_64-apple-darwin --release\n    else\n        cargo build --target aarch64-apple-darwin --release\n    fi\nelse\n    cargo build --target x86_64-pc-windows-msvc --release\nfi"
  },
  {
    "path": "tool/build_android.sh",
    "content": "#!/bin/bash\n\nif [[ \"$(uname -s)\" == \"Darwin\" ]]; then\n    export NDK_HOST_TAG=\"darwin-x86_64\"\nelif [[ \"$(uname -s)\" == \"Linux\" ]]; then\n    export NDK_HOST_TAG=\"linux-x86_64\"\nelse\n    echo \"Unsupported OS.\"\n    exit\nfi\n\nNDK=${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-\"$ANDROID_SDK_ROOT/ndk\"}}\nCOMPILER_DIR=\"$NDK/toolchains/llvm/prebuilt/$NDK_HOST_TAG/bin\"\nexport PATH=\"$COMPILER_DIR:$PATH\"\n\necho \"$COMPILER_DIR\"\n\nexport CC_i686_linux_android=$COMPILER_DIR/i686-linux-android21-clang\nexport AR_i686_linux_android=$COMPILER_DIR/llvm-ar\nexport CARGO_TARGET_I686_LINUX_ANDROID_LINKER=$COMPILER_DIR/i686-linux-android21-clang\nexport CARGO_TARGET_I686_LINUX_ANDROID_AR=$COMPILER_DIR/llvm-ar\n\nexport CC_x86_64_linux_android=$COMPILER_DIR/x86_64-linux-android21-clang\nexport AR_x86_64_linux_android=$COMPILER_DIR/llvm-ar\nexport CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=$COMPILER_DIR/x86_64-linux-android21-clang\nexport CARGO_TARGET_X86_64_LINUX_ANDROID_AR=$COMPILER_DIR/llvm-ar\n\nexport CC_armv7_linux_androideabi=$COMPILER_DIR/armv7a-linux-androideabi21-clang\nexport AR_armv7_linux_androideabi=$COMPILER_DIR/llvm-ar\nexport CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=$COMPILER_DIR/armv7a-linux-androideabi21-clang\nexport CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_AR=$COMPILER_DIR/llvm-ar\n\nexport CC_aarch64_linux_android=$COMPILER_DIR/aarch64-linux-android21-clang\nexport AR_aarch64_linux_android=$COMPILER_DIR/llvm-ar\nexport CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$COMPILER_DIR/aarch64-linux-android21-clang\nexport CARGO_TARGET_AARCH64_LINUX_ANDROID_AR=$COMPILER_DIR/llvm-ar\n\nif [ \"$1\" = \"x86\" ]; then\n  rustup target add i686-linux-android\n  cargo build --target i686-linux-android --release\n  mv \"target/i686-linux-android/release/libisar.so\" \"libisar_android_x86.so\"\nelif [ \"$1\" = \"x64\" ]; then\n  rustup target add x86_64-linux-android\n  cargo build --target x86_64-linux-android --release\n  mv \"target/x86_64-linux-android/release/libisar.so\" \"libisar_android_x64.so\"\nelif [ \"$1\" = \"armv7\" ]; then\n  rustup target add armv7-linux-androideabi\n  cargo build --target armv7-linux-androideabi  --release\n  mv \"target/armv7-linux-androideabi/release/libisar.so\" \"libisar_android_armv7.so\"\nelse\n  rustup target add aarch64-linux-android\n  cargo build --target aarch64-linux-android --release\n  mv \"target/aarch64-linux-android/release/libisar.so\" \"libisar_android_arm64.so\"\nfi\n\n\n\n\n\n\n"
  },
  {
    "path": "tool/build_ios.sh",
    "content": "export IPHONEOS_DEPLOYMENT_TARGET=12.0\n\nrustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios\ncargo build --target aarch64-apple-ios --release\ncargo build --target aarch64-apple-ios-sim --release\ncargo build --target x86_64-apple-ios --release\n\nlipo \"target/aarch64-apple-ios-sim/release/libisar.a\" \"target/x86_64-apple-ios/release/libisar.a\" -output \"target/aarch64-apple-ios-sim/libisar.a\" -create\nxcodebuild \\\n    -create-xcframework \\\n    -library target/aarch64-apple-ios/release/libisar.a \\\n    -library target/aarch64-apple-ios-sim/libisar.a \\\n    -output isar.xcframework \n\nzip -r isar_ios.xcframework.zip isar.xcframework"
  },
  {
    "path": "tool/build_linux.sh",
    "content": "if [ \"$1\" = \"x64\" ]; then\n  rustup target add target x86_64-unknown-linux-gnu\n  cargo build --target x86_64-unknown-linux-gnu --release\n  mv \"target/x86_64-unknown-linux-gnu/release/libisar.so\" \"libisar_linux_x64.so\"\nelse\n  rustup target add aarch64-unknown-linux-gnu\n  cargo build --target aarch64-unknown-linux-gnu --release\n  mv \"target/aarch64-unknown-linux-gnu/release/libisar.so\" \"libisar_linux_arm64.so\"\nfi"
  },
  {
    "path": "tool/build_macos.sh",
    "content": "export MACOSX_DEPLOYMENT_TARGET=10.11\n\nrustup target add aarch64-apple-darwin x86_64-apple-darwin\ncargo build --target aarch64-apple-darwin --release\ncargo build --target x86_64-apple-darwin --release\n\nlipo \"target/aarch64-apple-darwin/release/libisar.dylib\" \"target/x86_64-apple-darwin/release/libisar.dylib\" -output \"libisar_macos.dylib\" -create\ninstall_name_tool -id @rpath/libisar.dylib libisar_macos.dylib"
  },
  {
    "path": "tool/build_windows.sh",
    "content": "if [ \"$1\" = \"x64\" ]; then\n  rustup target add x86_64-pc-windows-msvc\n  cargo build --target x86_64-pc-windows-msvc --release\n  mv \"target/x86_64-pc-windows-msvc/release/isar.dll\" \"isar_windows_x64.dll\"\nelse\n  rustup target add aarch64-pc-windows-msvc\n  cargo build --target aarch64-pc-windows-msvc --release\n  mv \"target/aarch64-pc-windows-msvc/release/isar.dll\" \"isar_windows_arm64.dll\"\nfi"
  },
  {
    "path": "tool/cbindgen.toml",
    "content": "language = \"C\"\n\n[parse]\nparse_deps = true\ninclude = [\"isar-core\", \"isar\"]\n\n[parse.expand]\ncrates = [\"isar\"]"
  },
  {
    "path": "tool/download_binaries.sh",
    "content": "#!/bin/bash\n\nversion=`dart packages/isar/tool/get_version.dart`\nbinariesUrl=\"https://binaries.isar-community.dev/$ISAR_VERSION\"\n\n\ncurl \"${binariesUrl}/libisar_android_arm64.so\" -o packages/isar_flutter_libs/android/src/main/jniLibs/arm64-v8a/libisar.so --create-dirs -L -f\ncurl \"${binariesUrl}/libisar_android_armv7.so\" -o packages/isar_flutter_libs/android/src/main/jniLibs/armeabi-v7a/libisar.so --create-dirs -L -f\ncurl \"${binariesUrl}/libisar_android_x64.so\" -o packages/isar_flutter_libs/android/src/main/jniLibs/x86_64/libisar.so --create-dirs -L\ncurl \"${binariesUrl}/libisar_android_x86.so\" -o packages/isar_flutter_libs/android/src/main/jniLibs/x86/libisar.so --create-dirs -L -f\n\ncurl \"${binariesUrl}/isar_ios.xcframework.zip\" -o packages/isar_flutter_libs/ios/isar_ios.xcframework.zip --create-dirs -L -f\nunzip -o packages/isar_flutter_libs/ios/isar_ios.xcframework.zip -d packages/isar_flutter_libs/ios\nrm packages/isar_flutter_libs/ios/isar_ios.xcframework.zip\n\ncurl \"${binariesUrl}/libisar_macos.dylib\" -o packages/isar_flutter_libs/macos/libisar.dylib --create-dirs -L -f\ncurl \"${binariesUrl}/libisar_linux_x64.so\" -o packages/isar_flutter_libs/linux/libisar.so --create-dirs -L -f\ncurl \"${binariesUrl}/isar_windows_x64.dll\" -o packages/isar_flutter_libs/windows/isar.dll --create-dirs -L -f"
  },
  {
    "path": "tool/ffigen.yaml",
    "content": "name: IsarCoreBindings\noutput: \"lib/src/native/bindings.dart\"\nheaders:\n  entry-points:\n    - \"isar-dart.h\"\n  include-directives:\n    - '**isar-dart.h'\n\nstructs:\n  dependency-only: opaque\n  include:\n    - CObject\n    - CObjectSet\n    - CObjectCollectionSet\n    - CLink\n    - CLinkSet\n    - CObjectLinkSet\n  rename:\n    '^(?!C)(.*)': 'C$1'\n  \nunions:\n  dependency-only: opaque\n  include:\n    - 'isar*'\n\npreamble: |\n  // ignore_for_file: camel_case_types, non_constant_identifier_names\n"
  },
  {
    "path": "tool/generate_bindings.sh",
    "content": "#!/bin/sh\n\ncargo install cbindgen\n\ncbindgen --config tool/cbindgen.toml --crate isar --output packages/isar/isar-dart.h\n\ncd packages/isar\n\ndart pub get\ndart run ffigen --config ../../tool/ffigen.yaml\nrm isar-dart.h\n\ndart format --fix lib/src/native/bindings.dart\n"
  },
  {
    "path": "tool/prepare_tests.sh",
    "content": "cd packages/isar_test\n\nflutter pub get\ndart tool/generate_long_double_test.dart\ndart tool/generate_all_tests.dart\nflutter pub run build_runner build"
  },
  {
    "path": "tool/publish.sh",
    "content": "#!/bin/bash\n\n# Publishes the packages for a release to isar-community.dev using the artifacts from github\n# Prerequisite: unpub authenticated and token added:\n# `unpub_auth login && unpub_auth get | dart pub token add https://pub.isar-community.dev/`\n#\n\nset -o errexit\npushd packages/isar\ndart pub get\npopd\nsh tool/download_binaries.sh\n#dart pub token add --env-var=PUB_JSON https://pub.isar-community.dev/\npushd packages/isar\ndart pub publish --force\npopd\npushd packages/isar_generator\ndart pub publish --force\npopd\npushd packages/isar_flutter_libs\ndart pub publish --force\npopd\n\n"
  },
  {
    "path": "tool/replace-versions.sh",
    "content": "#!/bin/bash\n# This script replaces the version placeholder with the latest tag version on this branch\nif [ \"$ISAR_VERSION\" == \"\" ]; then\n  export ISAR_VERSION=`git describe --tags --abbrev=0`\n  echo \"ISAR_VERSION not defined in environment, using $ISAR_VERSION\"\nfi\nfind packages -type f -exec sed -i \"s/0.0.0-placeholder/$ISAR_VERSION/g\" {} +\nfind docs -type f -exec sed -i \"s/0.0.0-placeholder/$ISAR_VERSION/g\" {} +\n"
  }
]