{"id":47282702,"url":"https://github.com/tomtom215/duckdb-behavioral","last_synced_at":"2026-05-25T00:01:56.377Z","repository":{"id":343310058,"uuid":"1157414238","full_name":"tomtom215/duckdb-behavioral","owner":"tomtom215","description":"A DuckDB Community Extension to enable Behavioral Analytics, inspired by ClickHouse.","archived":false,"fork":false,"pushed_at":"2026-03-28T12:54:58.000Z","size":973,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-28T14:44:19.000Z","etag":null,"topics":["behavioral-patterns","clickops","community-extension","criterion-tests","duckdb","duckdb-extension","ffi","funnel-analytics","rust"],"latest_commit_sha":null,"homepage":"https://duckdb-behavioral.com/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomtom215.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T19:42:49.000Z","updated_at":"2026-03-28T12:55:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tomtom215/duckdb-behavioral","commit_stats":null,"previous_names":["tomtom215/duckdb-behavioral"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/tomtom215/duckdb-behavioral","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomtom215%2Fduckdb-behavioral","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomtom215%2Fduckdb-behavioral/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomtom215%2Fduckdb-behavioral/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomtom215%2Fduckdb-behavioral/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomtom215","download_url":"https://codeload.github.com/tomtom215/duckdb-behavioral/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomtom215%2Fduckdb-behavioral/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33455026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-24T19:21:36.376Z","status":"ssl_error","status_checked_at":"2026-05-24T19:21:10.562Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["behavioral-patterns","clickops","community-extension","criterion-tests","duckdb","duckdb-extension","ffi","funnel-analytics","rust"],"created_at":"2026-03-16T02:29:30.034Z","updated_at":"2026-05-25T00:01:56.368Z","avatar_url":"https://github.com/tomtom215.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/banner.svg\" alt=\"duckdb-behavioral\" width=\"600\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eBehavioral analytics functions for DuckDB, inspired by ClickHouse.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/tomtom215/duckdb-behavioral/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/tomtom215/duckdb-behavioral/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/tomtom215/duckdb-behavioral/actions/workflows/e2e.yml\"\u003e\u003cimg src=\"https://github.com/tomtom215/duckdb-behavioral/actions/workflows/e2e.yml/badge.svg\" alt=\"E2E Tests\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://crates.io/crates/duckdb-behavioral\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/duckdb-behavioral.svg\" alt=\"Crates.io\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.rust-lang.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/MSRV-1.87-blue.svg\" alt=\"MSRV: 1.87\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://tomtom215.github.io/duckdb-behavioral/\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs-mdBook-blue.svg\" alt=\"Documentation\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#functions\"\u003eFunctions\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#examples\"\u003eExamples\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#performance\"\u003ePerformance\u003c/a\u003e \u0026bull;\n  \u003ca href=\"https://tomtom215.github.io/duckdb-behavioral/\"\u003eDocumentation\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nProvides `sessionize`, `retention`, `window_funnel`, `sequence_match`,\n`sequence_count`, `sequence_match_events`, and `sequence_next_node` as a loadable\n[DuckDB](https://duckdb.org/) extension written in Rust. **Complete\n[ClickHouse](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions)\nbehavioral analytics parity.**\n\n\u003e **Personal Project Disclaimer**: This is a personal project developed on my own\n\u003e time. It is not affiliated with, endorsed by, or related to my employer or\n\u003e professional role in any way.\n\n\u003e **AI-Assisted Development**: Built with Claude (Anthropic). Correctness is\n\u003e validated by automated testing — not assumed from AI output. See [Quality](#quality).\n\n## Table of Contents\n\n- [Quick Start](#quick-start)\n- [Functions](#functions)\n- [Examples](#examples)\n- [Integrations](#integrations)\n- [Performance](#performance)\n- [Community Extension](#community-extension)\n- [Quality](#quality)\n- [ClickHouse Parity Status](#clickhouse-parity-status)\n- [Building](#building)\n- [Development](#development)\n- [Documentation](#documentation)\n- [Requirements](#requirements)\n- [License](#license)\n\n## Quick Start\n\n```sql\n-- Install from the DuckDB Community Extensions repository\nINSTALL behavioral FROM community;\nLOAD behavioral;\n```\n\nOr build from source:\n\n```bash\ncargo build --release\nduckdb -unsigned -cmd \"LOAD 'target/release/libbehavioral.so';\"\n```\n\n**Verify it works** — run these one-liners after loading:\n\n```sql\n-- Session IDs (should return 1)\nSELECT sessionize(TIMESTAMP '2024-01-01 10:00:00', INTERVAL '30 minutes') OVER () as session_id;\n\n-- Retention (should return [true, false])\nSELECT retention(true, false);\n\n-- Funnel progress (should return 2)\nSELECT window_funnel(INTERVAL '1 hour', TIMESTAMP '2024-01-01', true, true, false);\n```\n\n## Functions\n\n| Function | Signature | Returns | Description |\n|---|---|---|---|\n| `sessionize` | `(TIMESTAMP, INTERVAL)` | `BIGINT` | Window function assigning session IDs based on inactivity gaps |\n| `retention` | `(BOOLEAN, BOOLEAN, ...)` | `BOOLEAN[]` | Cohort retention analysis |\n| `window_funnel` | `(INTERVAL [, VARCHAR], TIMESTAMP, BOOLEAN, ...)` | `INTEGER` | Conversion funnel step tracking with [6 combinable modes](https://tomtom215.github.io/duckdb-behavioral/functions/window-funnel.html) |\n| `sequence_match` | `(VARCHAR, TIMESTAMP, BOOLEAN, ...)` | `BOOLEAN` | NFA-based [pattern matching](https://tomtom215.github.io/duckdb-behavioral/functions/sequence-match.html) over event sequences |\n| `sequence_count` | `(VARCHAR, TIMESTAMP, BOOLEAN, ...)` | `BIGINT` | Count non-overlapping pattern matches |\n| `sequence_match_events` | `(VARCHAR, TIMESTAMP, BOOLEAN, ...)` | `LIST(TIMESTAMP)` | Return matched condition timestamps |\n| `sequence_next_node` | `(VARCHAR, VARCHAR, TIMESTAMP, VARCHAR, BOOLEAN, ...)` | `VARCHAR` | Next event value after pattern match |\n\nAll functions support **2 to 32 boolean conditions**, matching ClickHouse's limit.\nDetailed documentation, examples, and edge case behavior for each function:\n[Function Reference](https://tomtom215.github.io/duckdb-behavioral/functions/sessionize.html)\n\n### Choosing the Right Function\n\n| I want to... | Use |\n|---|---|\n| Break events into sessions by inactivity gap | `sessionize` |\n| Check if users returned in later time periods | `retention` |\n| Measure how far users get through ordered steps | `window_funnel` |\n| Detect whether a pattern of events occurred | `sequence_match` |\n| Count how many times a pattern occurred | `sequence_count` |\n| Get timestamps of each matched pattern step | `sequence_match_events` |\n| Find what happened immediately after/before a pattern | `sequence_next_node` |\n\n## Examples\n\n### Conversion Funnel\n\nTrack how far users progress through a purchase flow within 1 hour:\n\n```sql\nSELECT user_id,\n  window_funnel(INTERVAL '1 hour', event_time,\n    event_type = 'page_view',\n    event_type = 'add_to_cart',\n    event_type = 'checkout',\n    event_type = 'purchase'\n  ) as furthest_step\nFROM events\nGROUP BY user_id;\n```\n\n### Session Analysis\n\nAssign session IDs with a 30-minute inactivity gap, then compute metrics:\n\n```sql\nWITH sessionized AS (\n    SELECT user_id, event_time,\n      sessionize(event_time, INTERVAL '30 minutes') OVER (\n        PARTITION BY user_id ORDER BY event_time\n      ) as session_id\n    FROM events\n)\nSELECT user_id, session_id,\n  COUNT(*) as page_views,\n  MIN(event_time) as session_start,\n  MAX(event_time) as session_end\nFROM sessionized\nGROUP BY user_id, session_id;\n```\n\n### Weekly Retention\n\nMeasure week-over-week retention for signup cohorts:\n\n```sql\nSELECT cohort_week,\n  COUNT(*) as cohort_size,\n  SUM(CASE WHEN r[1] THEN 1 ELSE 0 END) as week_0,\n  SUM(CASE WHEN r[2] THEN 1 ELSE 0 END) as week_1,\n  SUM(CASE WHEN r[3] THEN 1 ELSE 0 END) as week_2\nFROM (\n  SELECT user_id, cohort_week,\n    retention(\n      activity_date \u003e= cohort_week AND activity_date \u003c cohort_week + INTERVAL '7 days',\n      activity_date \u003e= cohort_week + INTERVAL '7 days' AND activity_date \u003c cohort_week + INTERVAL '14 days',\n      activity_date \u003e= cohort_week + INTERVAL '14 days' AND activity_date \u003c cohort_week + INTERVAL '21 days'\n    ) as r\n  FROM activity GROUP BY user_id, cohort_week\n)\nGROUP BY cohort_week ORDER BY cohort_week;\n```\n\n### Pattern Detection with Time Constraints\n\nFind users who viewed then purchased within 1 hour:\n\n```sql\nSELECT user_id,\n  sequence_match('(?1).*(?t\u003c=3600)(?2)', event_time,\n    event_type = 'page_view',\n    event_type = 'purchase'\n  ) as converted_within_hour\nFROM events GROUP BY user_id;\n```\n\n### Funnel Drop-off Report\n\nAggregate funnel results into a conversion report:\n\n```sql\nWITH funnels AS (\n  SELECT user_id,\n    window_funnel(INTERVAL '1 hour', event_time,\n      event_type = 'page_view', event_type = 'add_to_cart',\n      event_type = 'checkout', event_type = 'purchase'\n    ) as step\n  FROM events GROUP BY user_id\n)\nSELECT step as reached_step,\n  COUNT(*) as users,\n  ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 1) as pct\nFROM funnels GROUP BY step ORDER BY step;\n```\n\n### User Flow Analysis\n\nDiscover what page users visit after Home → Product:\n\n```sql\nSELECT\n  sequence_next_node('forward', 'first_match', event_time, page,\n    page = 'Home', page = 'Home', page = 'Product'\n  ) as next_page,\n  COUNT(*) as user_count\nFROM events GROUP BY ALL ORDER BY user_count DESC;\n```\n\n### Pattern Frequency\n\nCount how many times users repeat a view → cart cycle:\n\n```sql\nSELECT user_id,\n  sequence_count('(?1).*(?2)', event_time,\n    event_type = 'page_view',\n    event_type = 'add_to_cart'\n  ) as view_cart_cycles\nFROM events GROUP BY user_id ORDER BY view_cart_cycles DESC;\n```\n\n### Matched Event Timestamps\n\nGet the exact timestamps when each funnel step was satisfied:\n\n```sql\nSELECT user_id,\n  sequence_match_events('(?1).*(?2).*(?3)', event_time,\n    event_type = 'page_view',\n    event_type = 'add_to_cart',\n    event_type = 'purchase'\n  ) as step_timestamps\nFROM events GROUP BY user_id;\n```\n\nFor 5 complete real-world examples with sample data, see\n[Use Cases](https://tomtom215.github.io/duckdb-behavioral/use-cases.html).\nFor a comprehensive recipe collection, see\n[SQL Cookbook](https://tomtom215.github.io/duckdb-behavioral/cookbook.html).\n\n## Integrations\n\n### Python\n\n```python\nimport duckdb\n\nconn = duckdb.connect()\nconn.execute(\"INSTALL behavioral FROM community\")\nconn.execute(\"LOAD behavioral\")\n\ndf = conn.execute(\"\"\"\n    SELECT user_id,\n      window_funnel(INTERVAL '1 hour', event_time,\n        event_type = 'view', event_type = 'cart', event_type = 'purchase'\n      ) as steps\n    FROM events GROUP BY user_id\n\"\"\").fetchdf()\n```\n\n### Node.js\n\n```javascript\nconst duckdb = require('duckdb');\nconst db = new duckdb.Database(':memory:');\n\ndb.run(\"INSTALL behavioral FROM community\");\ndb.run(\"LOAD behavioral\");\n```\n\n### dbt\n\n```yaml\n# profiles.yml\nmy_project:\n  outputs:\n    dev:\n      type: duckdb\n      extensions:\n        - name: behavioral\n          repo: community\n```\n\n### Parquet / CSV / JSON\n\n```sql\n-- Query any file format directly\nSELECT user_id,\n  window_funnel(INTERVAL '1 hour', event_time,\n    event_type = 'view', event_type = 'purchase')\nFROM read_parquet('events/*.parquet')\nGROUP BY user_id;\n```\n\n## Performance\n\nAll measurements below are Criterion.rs 0.8.2 with 95% confidence intervals,\nvalidated across multiple runs on commodity hardware.\n\n| Function | Scale | Wall Clock | Throughput |\n|---|---|---|---|\n| `sessionize` | **1 billion** | **1.20 s** | **830 Melem/s** |\n| `retention` (combine) | 100 million | 274 ms | 365 Melem/s |\n| `window_funnel` | 100 million | 791 ms | 126 Melem/s |\n| `sequence_match` | 100 million | 1.05 s | 95 Melem/s |\n| `sequence_count` | 100 million | 1.18 s | 85 Melem/s |\n| `sequence_match_events` | 100 million | 1.07 s | 93 Melem/s |\n| `sequence_next_node` | 10 million | 546 ms | 18 Melem/s |\n\n**Key design choices:**\n\n- **16-byte `Copy` events** with `u32` bitmask conditions — four events per cache\n  line, zero heap allocation per event\n- **O(1) combine** for `sessionize` and `retention` via boundary tracking and\n  bitmask OR\n- **In-place combine** for event-collecting functions — O(N) amortized instead\n  of O(N^2) from repeated allocation\n- **NFA fast paths** — common pattern shapes dispatch to specialized O(n) linear\n  scans instead of full NFA backtracking\n- **Presorted detection** — O(n) check skips O(n log n) sort when events arrive\n  in timestamp order\n\n**Optimization highlights:**\n\n| Optimization | Speedup | Technique |\n|---|---|---|\n| Event bitmask | 5–13x | `Vec\u003cbool\u003e` replaced with `u32` bitmask, enabling `Copy` semantics |\n| In-place combine | up to 2,436x | O(N) amortized extend instead of O(N^2) merge-allocate |\n| NFA lazy matching | 1,961x at 1M events | Swapped exploration order so `.*` tries advancing before consuming |\n| `Arc\u003cstr\u003e` values | 2.1–5.8x | Reference-counted strings for O(1) clone in `sequence_next_node` |\n| NFA fast paths | 39–61% | Pattern classification dispatches common shapes to O(n) linear scans |\n\nFive attempted optimizations were measured, found to be regressions, and reverted.\nAll negative results are documented in [`PERF.md`](PERF.md).\n\nFull methodology, per-session optimization history with confidence intervals, and\nreproducible benchmark instructions: [`PERF.md`](PERF.md).\n\n## Community Extension\n\nThis extension is listed in the\n[DuckDB Community Extensions](https://github.com/duckdb/community-extensions)\nrepository ([PR #1306](https://github.com/duckdb/community-extensions/pull/1306),\nmerged 2026-02-15). Install with:\n\n```sql\nINSTALL behavioral FROM community;\nLOAD behavioral;\n```\n\nNo build tools, compilation, or `-unsigned` flag required.\n\n### Update Process\n\nThe [`community-submission.yml`](.github/workflows/community-submission.yml)\nworkflow automates the full pre-submission pipeline in 5 phases:\n\n| Phase | Purpose |\n|-------|---------|\n| Validate | `description.yml` schema, version consistency, required files |\n| Quality Gate | `cargo test`, `clippy`, `fmt`, `doc` |\n| Build \u0026 Test | `make configure \u0026\u0026 make release \u0026\u0026 make test_release` |\n| Pin Ref | Updates `description.yml` ref to the validated commit SHA |\n| Submission Package | Uploads artifact, generates step-by-step PR commands |\n\n### Updating the Published Extension\n\nPush changes to this repository, re-run the submission workflow to pin the new\nref, then open a new PR against `duckdb/community-extensions` updating the ref\nfield in `extensions/behavioral/description.yml`. When DuckDB releases a new\nversion, update `libduckdb-sys`, `TARGET_DUCKDB_VERSION`, and the\n`extension-ci-tools` submodule.\n\n## Quality\n\n| Metric | Value |\n|---|---|\n| Unit tests | 453 + 1 doc-test |\n| E2E tests | 11 workflow steps (2 platforms) + 59 SQL queries (against real DuckDB CLI) |\n| Property-based tests | 29 (proptest) |\n| Mutation testing | 88.4% kill rate (130/147, cargo-mutants) |\n| Clippy warnings | 0 (pedantic + nursery + cargo lint groups) |\n| CI jobs | 13 (check, test, clippy, fmt, doc, MSRV, bench-compile, deny, semver, coverage, cross-platform, extension-build, ci-gate) |\n| Benchmark files | 7 (Criterion.rs, up to 1 billion elements) |\n| Release platforms | 4 (Linux x86_64/ARM64, macOS x86_64/ARM64) |\n\nCI runs on every push and PR: 6 workflows across `.github/workflows/` including\nE2E tests against real DuckDB, CodeQL static analysis, SemVer validation, and\n4-platform release builds with provenance attestation.\n\n## ClickHouse Parity Status\n\n**COMPLETE** — All ClickHouse behavioral analytics functions are implemented.\n\n| Function | Status |\n|---|---|\n| `retention` | Complete |\n| `window_funnel` (6 modes) | Complete |\n| `sequence_match` | Complete |\n| `sequence_count` | Complete |\n| `sequence_match_events` | Complete |\n| `sequence_next_node` | Complete |\n| 32-condition support | Complete |\n| `sessionize` | Extension-only (no ClickHouse equivalent) |\n\n## Building\n\n**Prerequisites**: Rust 1.87+ (MSRV), a C compiler (for DuckDB sys bindings)\n\n```bash\n# Build the extension (release mode)\ncargo build --release\n\n# The loadable extension will be at:\n# target/release/libbehavioral.so   (Linux)\n# target/release/libbehavioral.dylib (macOS)\n```\n\n## Development\n\n```bash\ncargo test                  # Unit tests + doc-tests (453 + 1)\ncargo clippy --all-targets  # Zero warnings required\ncargo fmt -- --check        # Format check\ncargo bench                 # Criterion.rs benchmarks\ncargo doc --no-deps         # Build API documentation\n\n# Run all quality checks at once\n./scripts/check.sh\n\n# Build extension via community Makefile\ngit submodule update --init\nmake configure \u0026\u0026 make release \u0026\u0026 make test_release\n```\n\nThis project follows [Semantic Versioning](https://semver.org/).\nSee the [versioning policy](https://tomtom215.github.io/duckdb-behavioral/operations/security.html#versioning)\nfor the full SemVer rules applied to SQL function signatures.\n\n## Documentation\n\n- **[Getting Started](https://tomtom215.github.io/duckdb-behavioral/getting-started.html)** — installation, loading, troubleshooting\n- **[Function Reference](https://tomtom215.github.io/duckdb-behavioral/functions/sessionize.html)** — detailed docs for all 7 functions\n- **[Use Cases](https://tomtom215.github.io/duckdb-behavioral/use-cases.html)** — 5 complete real-world examples with sample data\n- **[SQL Cookbook](https://tomtom215.github.io/duckdb-behavioral/cookbook.html)** — practical recipes for common analytics patterns\n- **[Quick Reference](https://tomtom215.github.io/duckdb-behavioral/quick-reference.html)** — one-page cheat sheet for all functions and patterns\n- **[Engineering Overview](https://tomtom215.github.io/duckdb-behavioral/engineering.html)** — architecture, testing philosophy, design trade-offs\n- **[Performance](https://tomtom215.github.io/duckdb-behavioral/internals/performance.html)** — benchmarks, optimization history, methodology\n- **[ClickHouse Compatibility](https://tomtom215.github.io/duckdb-behavioral/internals/clickhouse-compatibility.html)** — syntax mapping, semantic parity\n- **[Contributing](https://tomtom215.github.io/duckdb-behavioral/contributing.html)** — development setup, testing, PR process\n\n## Requirements\n\n- Rust 1.87+ (MSRV)\n- DuckDB 1.5.3 (pinned dependency)\n- Python 3.x (for extension metadata tooling)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomtom215%2Fduckdb-behavioral","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomtom215%2Fduckdb-behavioral","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomtom215%2Fduckdb-behavioral/lists"}