diff --git a/ARCHITECTURAL_GUARDRAILS.md b/ARCHITECTURAL_GUARDRAILS.md index 7a9b6bfe..8540f6b4 100644 --- a/ARCHITECTURAL_GUARDRAILS.md +++ b/ARCHITECTURAL_GUARDRAILS.md @@ -38,7 +38,7 @@ The project is organized as a Cargo workspace with the following crates: | `sensibledb-cli` | `sensibledb-cli/` | CLI tool for project management and query deployment | | `sensibledb-explorer` | `sensibledb-explorer/` | Web-based data explorer UI | | `metrics` | `metrics/` | Observability and metrics collection | -| `nql-tests` | `nql-tests/` | SensibleQL query language test suite | +| `sensibleql-tests` | `sensibleql-tests/` | SensibleQL query language test suite | --- @@ -118,7 +118,7 @@ The project is organized as a Cargo workspace with the following crates: ## 8. Testing Guardrails - **Unit tests**: Co-located with source in each crate -- **Query tests**: `nql-tests/` crate for SensibleQL validation +- **Query tests**: `sensibleql-tests/` crate for SensibleQL validation - **E2E tests**: Playwright-based in `e2e/` directory - **Metrics**: Collected via `metrics/` crate diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index a1ff4343..dfa7673a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -126,7 +126,7 @@ The heart of SensibleDB containing all database functionality. - `analyzer/` - Type checking, validation, and diagnostics - `generator/` - Rust code generation from parsed queries -- **`grammar.pest`** - 295-line Pest grammar defining NQL syntax +- **`grammar.pest`** - 295-line Pest grammar defining SensibleQL syntax - **`protocol/`** - Wire protocol and data types @@ -232,8 +232,8 @@ sensibledb-cli/ #### `/sensibledb-macros/` - Procedural Macros Procedural macros for SensibleDB including route registration and code generation utilities. -#### `/nql-tests/` - NQL Test Suite -Test files for the Nexus Query Language (NQL). +#### `/sensibleql-tests/` - SensibleQL Test Suite +Test files for the Nexus Query Language (SensibleQL). #### `/metrics/` - Performance Metrics Performance benchmarking and metrics collection. @@ -284,7 +284,7 @@ Run Clippy to check code quality: The `clippy_check.sh` script at the repository root runs clippy with project-specific rules: - Treats warnings as errors -- Excludes `nql-tests` crate +- Excludes `sensibleql-tests` crate - Can run in dashboard mode with additional features ### Testing @@ -308,8 +308,8 @@ SensibleDB has a comprehensive test suite organized across multiple levels: - `init_tests.rs` - Project initialization - `project_tests.rs` - Project management -**NQL End-to-End Tests** -- `/nql-tests/tests/` - 54+ test directories covering: +**SensibleQL End-to-End Tests** +- `/sensibleql-tests/tests/` - 54+ test directories covering: - Graph operations (add_n, add_e, traversals) - Vector search (search_v_with_embed) - Text search (search_bm25) @@ -334,8 +334,8 @@ cargo test --workspace cargo test -p sensibledb-db cargo test -p sensibledb-cli -# Run NQL tests -cd nql-tests +# Run SensibleQL tests +cd sensibleql-tests ./test.sh # Run benchmarks diff --git a/Cargo.lock b/Cargo.lock index ed133eea..1e32ab0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3421,7 +3421,7 @@ dependencies = [ ] [[package]] -name = "nql-tests" +name = "sensibleql-tests" version = "0.1.0" dependencies = [ "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f31f02e0..883e5d7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "sensibledb-container", "sensibledb-macros", "sensibledb-cli", - "nql-tests", + "sensibleql-tests", "metrics", "sensibledb-explorer", ] diff --git a/README.md b/README.md index 28da134b..afa9ad41 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ SensibleDB/ ├── sensibledb-container/ # Docker container deployment ├── sensibledb-macros/ # Procedural macros for SensibleQL ├── metrics/ # Telemetry and metrics -├── nql-tests/ # Query language test suite +├── sensibleql-tests/ # Query language test suite ├── e2e/ # Playwright E2E tests (65 tests) └── docs/ # Documentation source ``` diff --git a/assets/sensible-db-logo.svg b/assets/sensible-db-logo.svg index 9a46efca..f2bda0bc 100644 --- a/assets/sensible-db-logo.svg +++ b/assets/sensible-db-logo.svg @@ -29,6 +29,6 @@ SensibleDB - EXPLORE YOUR DATA'S CONNECTIONS + EXPLORE YOUR DATA diff --git a/clippy_check.sh b/clippy_check.sh index c4123082..88c36cd2 100755 --- a/clippy_check.sh +++ b/clippy_check.sh @@ -1,5 +1,5 @@ -# cargo clippy --workspace --locked --exclude nql-tests --exclude metrics -- -D warnings -A clippy::too_many_arguments -A clippy::let-and-return -A clippy::module-inception -A clippy::new-ret-no-self -A clippy::wrong-self-convention -A clippy::large-enum-variant -A clippy::inherent-to-string -A clippy::inherent_to_string_shadow_display -D clippy::unwrap_used +# cargo clippy --workspace --locked --exclude sensibleql-tests --exclude metrics -- -D warnings -A clippy::too_many_arguments -A clippy::let-and-return -A clippy::module-inception -A clippy::new-ret-no-self -A clippy::wrong-self-convention -A clippy::large-enum-variant -A clippy::inherent-to-string -A clippy::inherent_to_string_shadow_display -D clippy::unwrap_used if [ "$1" = "dashboard" ]; then cargo clippy -p sensibledb-container --features dev \ @@ -14,7 +14,7 @@ if [ "$1" = "dashboard" ]; then -A clippy::inherent_to_string_shadow_display fi -cargo clippy --workspace --locked --exclude nql-tests --exclude sensibledb-cli --exclude sensibledb-explorer \ +cargo clippy --workspace --locked --exclude sensibleql-tests --exclude sensibledb-cli --exclude sensibledb-explorer \ -- -D warnings \ -A clippy::too_many_arguments \ -A clippy::let-and-return \ diff --git a/docs/INDEX.md b/docs/INDEX.md index 975a4cb9..9a489f18 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -1,449 +1,143 @@ -# SensibleDB Documentation - -## Table of Contents - -- [Getting Started](#getting-started) - - [Overview](#overview) - - [Installation](#installation) -- [SensibleQL Query Language](#sensibleql-query-language) - - [Overview](#sensibleql-overview) - - [Schema Definition](#schema-definition) - - [CRUD Operations](#crud-operations) - - [Graph Traversals](#graph-traversals) - - [Vector Operations](#vector-operations) -- [CLI Reference](#cli-reference) -- [SDKs](#sdks) -- [Features](#features) +# SensibleDB ---- - -## Getting Started - -### Overview - -SensibleDB is a high-performance **graph-vector database** built from scratch in Rust, with its own query language designed for traversing and manipulating graph and vector data efficiently. +**Embedded Graph-Vector Database for AI Applications** -SensibleDB makes it easy to build all components needed for an AI application in a single platform. You no longer need a separate application DB, vector DB, graph DB, or application layers. Just use SensibleDB. +SensibleDB is a unified database that makes it easy to build all the components needed for an AI application in a single platform. You no longer need a separate application DB, vector DB, graph DB, or application layers managing multiple storage locations. -#### Key Features +Built in Rust with LMDB as its storage engine — like SQLite for knowledge graphs. -- **Built-in MCP Tools** — AI agents can discover data and walk the graph autonomously -- **Built-in Embeddings** — Use `Embed()` to vectorize text directly in queries -- **RAG Tooling** — Vector search, keyword search (BM25), and graph traversals -- **Secure by Default** — Private by default, accessible only through compiled queries -- **Ultra-Low Latency** — Rust + LMDB for near-zero overhead access -- **Type-Safe Queries** — 100% type-safe with compile-time validation - -#### Multi-Model Support +--- -| Model | Description | Use Case | -|-------|-------------|----------| -| **Graph** | Native node/edge with traversals | Knowledge graphs, social networks | -| **Vector** | Cosine similarity with embeddings | Semantic search, RAG | -| **KV** | Simple key-value lookups | Caching, configuration | -| **Document** | Flexible schema documents | Content management | -| **Relational** | Table-based queries with joins | Traditional data relationships | +## Why SensibleDB? -### Installation +| Before SensibleDB | With SensibleDB | +|---|---| +| PostgreSQL for app data | **One database** handles everything | +| Pinecone / Weaviate for vectors | Graph + vector + embedded in a single engine | +| Neo4j for graph relationships | Type-safe queries with compile-time validation | +| Custom auth & access layers | Secure by default — access only through compiled SensibleQL | -#### Prerequisites +## Key Features -- **Rust** 1.75.0 or higher -- **Docker Desktop** (for local development) -- **LMDB** system library +- **Built-in MCP Tools** — AI agents discover data and walk the graph instead of generating SQL +- **Built-in Embeddings** — Vectorize text directly in queries with `Embed()` +- **RAG Tooling** — Vector search, keyword search, and graph traversals out of the box +- **Secure by Default** — Private by default; access only through compiled SensibleQL queries +- **Ultra-Low Latency** — Rust + LMDB storage engine for extreme performance +- **Type-Safe Queries** — SensibleQL is 100% type-safe, catch errors at compile time +- **Embedded Mode** — Zero external dependencies, use directly in Rust applications +- **Visual Explorer** — Interactive graph visualization, natural language chat, and report generation -```bash -# Install Rust -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +--- -# Install LMDB -brew install lmdb # macOS -apt install liblmdb-dev # Ubuntu/Debian -pacman -S lmdb # Arch Linux -``` +## Quick Start -#### Step 1: Install NexusCLI +### 1. Install the CLI ```bash curl -sSL "https://install.sensibledb-db.com" | bash -nexus --version ``` -#### Step 2: Initialize a Project +### 2. Initialize a Project ```bash mkdir my-project && cd my-project -nexus init +sensibledb init ``` -Creates: -``` -my-project/ -├── sensibledb.toml # Project configuration -├── db/ -│ ├── schema.hx # Schema definitions -│ └── queries.hx # Query definitions -└── .sensibledb/ # Build artifacts -``` +### 3. Write Your Schema & Queries -#### Step 3: Write Schema and Queries +Open the generated `.hx` file and define your schema: -**schema.hx:** -```nql +```sensibleql N::User { INDEX name: String, - email: String, - created_at: Date DEFAULT NOW + age: U32 } -E::Follows { - From: User, - To: User, - Properties: { - since: Date - } +E::FRIENDS { + from: User, + to: User } -``` -**queries.hx:** -```nql -QUERY createUser(name: String, email: String) => - user <- AddN({name: name, email: email}) +QUERY getUser(user_name: String) => + user <- N({name: user_name}) RETURN user -QUERY getUser(name: String) => - user <- N({name: name}) - RETURN user - -QUERY getUserFollowers(user_id: ID) => - followers <- N(user_id)::In - RETURN followers +QUERY getFriends(user_id: U64) => + user <- N({id: user_id}) + friends <- TRAVERSE user -[FRIENDS]-> friend + RETURN friend ``` -#### Step 4: Check and Deploy +### 4. Deploy ```bash -nexus check # Validate compilation -nexus push dev # Deploy locally +sensibledb push dev ``` -#### Step 5: Test +### 5. Use in Your Application -```bash -curl -X POST http://localhost:6969/createUser \ - -H 'Content-Type: application/json' \ - -d '{"name": "John", "email": "john@example.com"}' - -curl -X POST http://localhost:6969/getUser \ - -H 'Content-Type: application/json' \ - -d '{"name": "John"}' -``` - ---- - -## SensibleQL Query Language - -### Overview - -SensibleQL is a **strongly typed, compiled query language** for SensibleDB that combines the best features of Gremlin, Cypher, and Rust. - -#### Why SensibleQL? - -| Feature | SensibleQL | Gremlin | Cypher | -|---------|---------|---------|--------| -| Type Safety | Compile-time | Runtime | Runtime | -| Syntax | Clean, concise | Verbose | Readable | -| Performance | Compiled | Interpreted | Interpreted | -| IDE Support | Autocomplete | Limited | Limited | -| Vector Support | Built-in | External | External | - -#### Query Structure - -```nql -QUERY QueryName(param1: Type, param2: Type) => - result <- traversal_expression - RETURN result -``` - -| Component | Description | -|-----------|-------------| -| `QUERY` | Start query definition | -| `QueryName` | Query identifier (becomes API endpoint) | -| `param: Type` | Typed input parameters | -| `=>` | Separates header from body | -| `<-` | Assignment operator | -| `RETURN` | Output values | - -### Schema Definition - -#### Node Schema - -```nql -N::User { - INDEX name: String, - email: String, - age: U32, - created_at: Date DEFAULT NOW -} -``` - -#### Edge Schema - -```nql -E::Follows { - From: User, - To: User, - Properties: { - since: Date - } -} -``` - -#### Supported Types - -| Type | Description | Example | -|------|-------------|---------| -| `String` | UTF-8 text | `"hello"` | -| `I32`, `I64` | Signed integers | `42` | -| `U8`, `U32`, `U64` | Unsigned integers | `42` | -| `F32`, `F64` | Floating point | `3.14` | -| `Boolean` | True/false | `true` | -| `Date` | Timestamp | `NOW` | -| `ID` | Unique identifier | Auto-generated | -| `Vector` | Float array | `[0.1, 0.2, ...]` | - -### CRUD Operations - -#### Create - -```nql -QUERY createUser(name: String, email: String) => - user <- AddN({name: name, email: email}) - RETURN user - -QUERY followUser(from_id: ID, to_id: ID, since: Date) => - edge <- AddE({since: since})::From(from_id)::To(to_id) - RETURN edge -``` - -#### Read - -```nql -QUERY getUser(name: String) => - user <- N({name: name}) - RETURN user - -QUERY getAllUsers() => - users <- N() - RETURN users -``` - -#### Update - -```nql -QUERY updateUserEmail(user_id: ID, email: String) => - updated <- N(user_id)::Update({email: email}) - RETURN updated -``` - -#### Delete - -```nql -QUERY deleteUser(user_id: ID) => - N(user_id)::Drop - RETURN "Deleted" -``` - -### Graph Traversals - -#### `::Out` — Outgoing Nodes - -```nql -QUERY GetUserFollowing(user_id: ID) => - following <- N(user_id)::Out - RETURN following -``` - -#### `::In` — Incoming Nodes - -```nql -QUERY GetUserFollowers(user_id: ID) => - followers <- N(user_id)::In - RETURN followers -``` - -#### `::OutE` — Outgoing Edges - -```nql -QUERY GetFollowingEdges(user_id: ID) => - edges <- N(user_id)::OutE - RETURN edges -``` - -#### `::InE` — Incoming Edges - -```nql -QUERY GetFollowerEdges(user_id: ID) => - edges <- N(user_id)::InE - RETURN edges -``` - -#### Chaining Traversals - -```nql -QUERY GetFriendsOfFriends(user_id: ID) => - fof <- N(user_id)::Out::Out - RETURN fof -``` - -#### Shortest Path - -```nql -QUERY FindPath(from_id: ID, to_id: ID) => - path <- N(from_id)::ShortestPath(to_id)> - RETURN path -``` - -### Vector Operations - -#### Vector Similarity Search - -```nql -QUERY searchSimilar(query_vec: [F32], limit: U32) => - results <- SearchV
({vector: query_vec, limit: limit}) - RETURN results -``` - -#### Automatic Embeddings - -```nql -QUERY searchArticles(query: String) => - results <- SearchV
({vector: Embed(query), limit: 10}) - RETURN results -``` - -#### Keyword Search (BM25) - -```nql -QUERY keywordSearch(query: String) => - results <- SearchBM25
({fields: ["title", "content"], query: query}) - RETURN results -``` - -#### Hybrid Search with RRF - -```nql -QUERY hybridSearch(query: String) => - vector_results <- SearchV
({vector: Embed(query), limit: 20}) - keyword_results <- SearchBM25
({fields: ["title"], query: query}) - combined <- vector_results::RRF(keyword_results) - RETURN combined -``` - -#### MMR Reranking - -```nql -QUERY diverseResults(query: String) => - results <- SearchV
({vector: Embed(query), limit: 50}) - diverse <- results::MMR({diversity: 0.5, limit: 10}) - RETURN diverse -``` - ---- - -## CLI Reference - -### Commands - -| Command | Description | -|---------|-------------| -| `nexus init` | Initialize a new project | -| `nexus check` | Validate schema and queries | -| `nexus push dev` | Deploy to local instance | -| `nexus status` | Show instance status | -| `nexus start ` | Start an instance | -| `nexus stop ` | Stop an instance | -| `nexus logs` | Stream instance logs | -| `nexus prune` | Clean up unused resources | -| `nexus update` | Update CLI to latest version | - -### Configuration (sensibledb.toml) - -```toml -[project] -name = "my-project" -build_mode = "debug" - -[vector] -dimensions = 384 -metric = "cosine" - -[instance] -name = "dev-instance" -port = 6969 -``` - ---- - -## SDKs - -### TypeScript - -```bash -npm install sensible-ts -``` +**TypeScript:** ```typescript import SensibleDB from "sensible-ts"; + const client = new SensibleDB(); -const user = await client.query("getUser", { name: "John" }); -``` -### Python +// Create a user +await client.query("addUser", { name: "John", age: 30 }); -```bash -pip install sensible-py +// Query with type safety +const user = await client.query("getUser", { user_name: "John" }); ``` +**Python:** + ```python -from nexus import Client -client = Client(local=True, port=6969) -user = client.query("getUser", {"name": "John"}) -``` +from sensibledb import SensibleDB -### Rust (Embedded) +client = SensibleDB() -```toml -[dependencies] -sensibledb-db = { version = "1.3", features = ["embedded"] } +# Query the database +user = client.query("getUser", user_name="John") +print(user) ``` +**Rust (Embedded):** + ```rust use sensibledb_db::embedded::{Database, Node}; + let db = Database::open("./my_db")?; +let mut tx = db.write_transaction()?; + +tx.put_node(Node { + id: 1, + label: "User".into(), +})?; + +tx.commit()?; ``` --- -## Features - -### Built-in MCP Tools -AI agents can discover data and walk the graph autonomously, constructing queries based on graph topology. +## Documentation -### Built-in Embeddings -Use `Embed()` directly in queries — no external embedding service needed. +| Section | Description | +|---|---| +| [Getting Started](getting-started/intro.md) | Installation and setup | +| [SensibleQL](sensibleql/overview.md) | Query language reference | +| [CLI](cli/getting-started.md) | Command-line tool guide | +| [SDKs](sdks/overview.md) | TypeScript, Python, and Rust SDKs | +| [Features](features/overview.md) | Full feature overview | -### RAG Tooling -Vector search, BM25 keyword search, graph traversals, hybrid search with RRF, and MMR reranking. +--- -### Security -Private by default. Data accessible only through compiled SensibleQL queries. Type-safe queries prevent injection attacks. +## License -### Ultra-Low Latency -Rust + LMDB memory-mapped B-trees for near-zero overhead access. +SensibleDB is licensed under the AGPL (Affero General Public License). -### Type-Safe Queries -Compile-time validation catches errors before production. IDE support with autocomplete. +## Commercial Support -### Multi-Model -Graph, vector, KV, document, and relational — all in one database. +SensibleDB is available as a managed service for selected users. For enterprise support or managed deployment, [contact us](mailto:founders@sensibledb-db.com). diff --git a/docs/architecture/diagrams/system-context.mmd b/docs/architecture/diagrams/system-context.mmd index 2fe3ef01..0656cf1a 100644 --- a/docs/architecture/diagrams/system-context.mmd +++ b/docs/architecture/diagrams/system-context.mmd @@ -4,7 +4,7 @@ title: SensibleDB - System Context Diagram (C4 Level 1) graph TB subgraph External["External Systems"] - Developer["👤 Developer
Writes NexusQL schemas & queries"] + Developer["👤 Developer
Writes SensibleQL schemas & queries"] AIApp["🤖 AI Application
RAG, Agents, LLM Apps"] MCPClient["🔧 MCP Client
AI Agent Tool Discovery"] end diff --git a/docs/design/explorer-redesign.md b/docs/design/explorer-redesign.md index 125f1d0d..a62d2f78 100644 --- a/docs/design/explorer-redesign.md +++ b/docs/design/explorer-redesign.md @@ -415,7 +415,7 @@ Step 4: Processing | Label/Type | Type | "What kind of item this is" | | Vector Embedding | Similarity | "Items that are alike are grouped together" | | Graph Traversal | Follow connections | "Starting from one item, see what it connects to" | -| NQL Query | Ask a question | "A way to ask your data questions" | +| SensibleQL Query | Ask a question | "A way to ask your data questions" | | Schema | Structure | "The blueprint of how your data is organized" | ### 6.2 Contextual Tooltips @@ -539,7 +539,7 @@ Every technical term gets a `?` icon that shows: ### Phase 1: Foundation (Week 1) - [ ] Design system tokens (CSS variables) ✅ started - [ ] Consistent component styles (buttons, inputs, cards) -- [ ] Fix NQL query execution (camelCase params) ✅ done +- [ ] Fix SensibleQL query execution (camelCase params) ✅ done - [ ] Graph interaction (zoom, pan, drag, select) ✅ done ### Phase 2: Home & Onboarding (Week 2) @@ -550,7 +550,7 @@ Every technical term gets a `?` icon that shows: ### Phase 3: Chat Interface (Week 3) - [ ] Chat view with conversation history -- [ ] Natural language to NQL translation (local) +- [ ] Natural language to SensibleQL translation (local) - [ ] Streaming response display - [ ] Follow-up question suggestions @@ -585,4 +585,4 @@ Every technical term gets a `?` icon that shows: | Query success rate | > 90% | % of queries that return results vs errors | | User retention (7-day) | > 60% | Analytics: returning users / total users | | Support tickets | < 5/week | Track user-reported issues | -| NQL usage vs Chat | < 30% NQL | Migration to natural language interface | +| SensibleQL usage vs Chat | < 30% SensibleQL | Migration to natural language interface | diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index d5674c2e..a53cb7bd 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -25,7 +25,7 @@ nexus init ### 3. Write Schema and Queries **schema.hx:** -```nql +```sensibleql N::User { INDEX name: String, email: String, @@ -40,7 +40,7 @@ E::Follows { ``` **queries.hx:** -```nql +```sensibleql QUERY createUser(name: String, email: String) => user <- AddN({name: name, email: email}) RETURN user diff --git a/docs/nexusql/crud-operations.md b/docs/sensibleql/crud-operations.md similarity index 91% rename from docs/nexusql/crud-operations.md rename to docs/sensibleql/crud-operations.md index 52351ceb..09cc18e4 100644 --- a/docs/nexusql/crud-operations.md +++ b/docs/sensibleql/crud-operations.md @@ -2,7 +2,7 @@ ## Create -```nql +```sensibleql QUERY createUser(name: String, email: String) => user <- AddN({name: name, email: email}) RETURN user @@ -14,7 +14,7 @@ QUERY followUser(from_id: ID, to_id: ID, since: Date) => ## Read -```nql +```sensibleql QUERY getUser(name: String) => user <- N({name: name}) RETURN user @@ -22,7 +22,7 @@ QUERY getUser(name: String) => ## Update -```nql +```sensibleql QUERY updateUserEmail(user_id: ID, email: String) => updated <- N(user_id)::Update({email: email}) RETURN updated @@ -30,7 +30,7 @@ QUERY updateUserEmail(user_id: ID, email: String) => ## Delete -```nql +```sensibleql QUERY deleteUser(user_id: ID) => N(user_id)::Drop RETURN "Deleted" diff --git a/docs/nexusql/overview.md b/docs/sensibleql/overview.md similarity index 96% rename from docs/nexusql/overview.md rename to docs/sensibleql/overview.md index 755f9a97..48617f3f 100644 --- a/docs/nexusql/overview.md +++ b/docs/sensibleql/overview.md @@ -12,7 +12,7 @@ SensibleQL is a **strongly typed, compiled query language** for SensibleDB. ## Query Structure -```nql +```sensibleql QUERY QueryName(param1: Type, param2: Type) => result <- traversal_expression RETURN result @@ -20,7 +20,7 @@ QUERY QueryName(param1: Type, param2: Type) => ## Example -```nql +```sensibleql N::User { INDEX name: String, age: U32 diff --git a/docs/nexusql/schema-definition.md b/docs/sensibleql/schema-definition.md similarity index 95% rename from docs/nexusql/schema-definition.md rename to docs/sensibleql/schema-definition.md index 88dabd7e..420320d5 100644 --- a/docs/nexusql/schema-definition.md +++ b/docs/sensibleql/schema-definition.md @@ -2,7 +2,7 @@ ## Node Schema -```nql +```sensibleql N::User { INDEX name: String, email: String, @@ -13,7 +13,7 @@ N::User { ## Edge Schema -```nql +```sensibleql E::Follows { From: User, To: User, diff --git a/docs/nexusql/traversals.md b/docs/sensibleql/traversals.md similarity index 90% rename from docs/nexusql/traversals.md rename to docs/sensibleql/traversals.md index 75cbccb9..5ce5018c 100644 --- a/docs/nexusql/traversals.md +++ b/docs/sensibleql/traversals.md @@ -2,7 +2,7 @@ ## Out - Outgoing Nodes -```nql +```sensibleql QUERY GetUserFollowing(user_id: ID) => following <- N(user_id)::Out RETURN following @@ -10,7 +10,7 @@ QUERY GetUserFollowing(user_id: ID) => ## In - Incoming Nodes -```nql +```sensibleql QUERY GetUserFollowers(user_id: ID) => followers <- N(user_id)::In RETURN followers @@ -18,7 +18,7 @@ QUERY GetUserFollowers(user_id: ID) => ## OutE - Outgoing Edges -```nql +```sensibleql QUERY GetFollowingEdges(user_id: ID) => edges <- N(user_id)::OutE RETURN edges @@ -26,7 +26,7 @@ QUERY GetFollowingEdges(user_id: ID) => ## InE - Incoming Edges -```nql +```sensibleql QUERY GetFollowerEdges(user_id: ID) => edges <- N(user_id)::InE RETURN edges @@ -34,7 +34,7 @@ QUERY GetFollowerEdges(user_id: ID) => ## Chaining -```nql +```sensibleql QUERY GetFriendsOfFriends(user_id: ID) => fof <- N(user_id)::Out::Out RETURN fof @@ -42,7 +42,7 @@ QUERY GetFriendsOfFriends(user_id: ID) => ## Shortest Path -```nql +```sensibleql QUERY FindPath(from_id: ID, to_id: ID) => path <- N(from_id)::ShortestPath(to_id)> RETURN path diff --git a/docs/nexusql/vector-operations.md b/docs/sensibleql/vector-operations.md similarity index 93% rename from docs/nexusql/vector-operations.md rename to docs/sensibleql/vector-operations.md index 30d2dd9c..e79ae656 100644 --- a/docs/nexusql/vector-operations.md +++ b/docs/sensibleql/vector-operations.md @@ -2,7 +2,7 @@ ## Vector Similarity Search -```nql +```sensibleql QUERY searchSimilar(query_vec: [F32], limit: U32) => results <- SearchV
({vector: query_vec, limit: limit}) RETURN results @@ -10,7 +10,7 @@ QUERY searchSimilar(query_vec: [F32], limit: U32) => ## Automatic Embeddings -```nql +```sensibleql QUERY searchArticles(query: String) => results <- SearchV
({vector: Embed(query), limit: 10}) RETURN results @@ -18,7 +18,7 @@ QUERY searchArticles(query: String) => ## Keyword Search (BM25) -```nql +```sensibleql QUERY keywordSearch(query: String) => results <- SearchBM25
({fields: ["title", "content"], query: query}) RETURN results @@ -26,7 +26,7 @@ QUERY keywordSearch(query: String) => ## Hybrid Search with RRF -```nql +```sensibleql QUERY hybridSearch(query: String) => vector_results <- SearchV
({vector: Embed(query), limit: 20}) keyword_results <- SearchBM25
({fields: ["title"], query: query}) diff --git a/e2e/chat-view.spec.ts b/e2e/chat-view.spec.ts index f605eb9a..e1ced9dd 100644 --- a/e2e/chat-view.spec.ts +++ b/e2e/chat-view.spec.ts @@ -59,12 +59,12 @@ test.describe('Chat Interface', () => { await expect(page.locator('.follow-up-chips')).toBeVisible(); }); - test('"How did I get this?" expandable shows NQL query', async ({ page }) => { + test('"How did I get this?" expandable shows SensibleQL query', async ({ page }) => { const input = page.locator('.chat-input-area input'); await input.fill('What data do I have?'); await page.getByRole('button', { name: 'Send' }).click(); await expect(page.locator('.chat-message.assistant').last()).toBeVisible({ timeout: 5000 }); - const nqlToggle = page.locator('.nql-toggle-btn'); - await expect(nqlToggle).toBeVisible(); + const sensibleqlToggle = page.locator('.sensibleql-toggle-btn'); + await expect(sensibleqlToggle).toBeVisible(); }); }); diff --git a/e2e/fixtures.ts b/e2e/fixtures.ts index 3cabfb2e..d7ac189d 100644 --- a/e2e/fixtures.ts +++ b/e2e/fixtures.ts @@ -83,7 +83,7 @@ export async function injectTauriMock(page: Page) { return 'deleted'; case 'schema_get': return mockData.schema; - case 'nql_execute': { + case 'sensibleql_execute': { const query = (args?.query || '').trim().toUpperCase(); if (query.includes('COUNT') && query.includes('NODE')) { return { success: true, message: `Found ${mockData.nodes.length} nodes`, data: { nodes: mockData.nodes, edges: [] } }; diff --git a/e2e/onboarding-and-polish.spec.ts b/e2e/onboarding-and-polish.spec.ts index 57bf1082..1ce4c295 100644 --- a/e2e/onboarding-and-polish.spec.ts +++ b/e2e/onboarding-and-polish.spec.ts @@ -71,15 +71,15 @@ test.describe('Design System', () => { }); }); -test.describe('NQL Editor', () => { +test.describe('SensibleQL Editor', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); - await page.getByRole('button', { name: 'NQL Editor' }).first().click(); + await page.getByRole('button', { name: 'SensibleQL Editor' }).first().click(); await page.waitForTimeout(500); }); - test('renders NQL editor', async ({ page }) => { - await expect(page.locator('.nql-editor')).toBeVisible(); + test('renders SensibleQL editor', async ({ page }) => { + await expect(page.locator('.sensibleql-editor')).toBeVisible(); }); test('displays sample queries', async ({ page }) => { diff --git a/mkdocs.yml b/mkdocs.yml index 12f2f7a9..52d95f2b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,6 +43,7 @@ markdown_extensions: - pymdownx.details nav: + - Home: index.md - Getting Started: - Overview: getting-started/intro.md - Installation: getting-started/installation.md diff --git a/sensibledb-cli/src/github_issue.rs b/sensibledb-cli/src/github_issue.rs index 2237b551..1c26e43e 100644 --- a/sensibledb-cli/src/github_issue.rs +++ b/sensibledb-cli/src/github_issue.rs @@ -53,8 +53,8 @@ impl GitHubIssueBuilder { /// Build the GitHub issue URL with query parameters. pub fn build_url(&self) -> String { let title = match &self.first_error { - Some(error) => format!("bug (nql): rust generation failure - {}", error), - None => "bug (nql): rust generation failure".to_string(), + Some(error) => format!("bug (sensibleql): rust generation failure - {}", error), + None => "bug (sensibleql): rust generation failure".to_string(), }; // URL encode the fixed parameters diff --git a/sensibledb-db/src/sensibledbc/generator/queries.rs b/sensibledb-db/src/sensibledbc/generator/queries.rs index d07482c7..30601075 100644 --- a/sensibledb-db/src/sensibledbc/generator/queries.rs +++ b/sensibledb-db/src/sensibledbc/generator/queries.rs @@ -300,7 +300,7 @@ impl Query { } } else if struct_def.is_collection { // Collection - generate mapping code - // Use NQL closure param name if available, otherwise fall back to singular form + // Use SensibleQL closure param name if available, otherwise fall back to singular form let singular_var = struct_def .closure_param_name .as_deref() @@ -1310,7 +1310,7 @@ impl Query { )?; } else if struct_def.is_collection { // Collection - generate mapping code - // Use NQL closure param name if available, otherwise fall back to singular form + // Use SensibleQL closure param name if available, otherwise fall back to singular form let singular_var = struct_def .closure_param_name .as_deref() diff --git a/sensibledb-db/src/sensibledbc/generator/return_values.rs b/sensibledb-db/src/sensibledbc/generator/return_values.rs index 2f10c2d4..4e066b29 100644 --- a/sensibledb-db/src/sensibledbc/generator/return_values.rs +++ b/sensibledb-db/src/sensibledbc/generator/return_values.rs @@ -53,7 +53,7 @@ pub struct ReturnValueStruct { pub field_infos: Vec, // Original field info for nested struct generation pub aggregate_properties: Vec, // Properties to group by (for closure-style aggregates) pub is_count_aggregate: bool, // True for COUNT mode aggregates - pub closure_param_name: Option, // NQL closure parameter name (e.g., "e" from entries::|e|) + pub closure_param_name: Option, // SensibleQL closure parameter name (e.g., "e" from entries::|e|) pub is_primitive: bool, // True for Count/Boolean/Scalar - emit variable directly pub primitive_literal_value: Option>, // For primitives with field access (e.g., user::ID) } diff --git a/sensibledb-db/src/sensibledbc/generator/traversal_steps.rs b/sensibledb-db/src/sensibledbc/generator/traversal_steps.rs index e85950a2..5d859658 100644 --- a/sensibledb-db/src/sensibledbc/generator/traversal_steps.rs +++ b/sensibledb-db/src/sensibledbc/generator/traversal_steps.rs @@ -140,7 +140,7 @@ pub struct Traversal { pub excluded_fields: Vec, pub nested_traversals: std::collections::HashMap, pub is_reused_variable: bool, - pub closure_param_name: Option, // NQL closure parameter name (e.g., "e" from entries::|e|) + pub closure_param_name: Option, // SensibleQL closure parameter name (e.g., "e" from entries::|e|) /// Maps output field name -> source property name for renamed fields /// e.g., "post" -> "content" for `post: content`, "file_id" -> "ID" pub field_name_mappings: std::collections::HashMap, diff --git a/sensibledb-db/src/sensibledbc/parser/README.md b/sensibledb-db/src/sensibledbc/parser/README.md index b10640d8..ebb75b6f 100644 --- a/sensibledb-db/src/sensibledbc/parser/README.md +++ b/sensibledb-db/src/sensibledbc/parser/README.md @@ -7,7 +7,7 @@ The parser module transforms SensibleQL source code into an Abstract Syntax Tree ### Core Components - **`mod.rs`** - Main parser entry point, orchestrates parsing of schemas, queries, and migrations -- **`grammar.pest`** - Pest grammar defining NQL syntax rules +- **`grammar.pest`** - Pest grammar defining SensibleQL syntax rules - **`types.rs`** - AST node definitions and data structures - **`location.rs`** - Location tracking for error reporting @@ -24,7 +24,7 @@ The parser module transforms SensibleQL source code into an Abstract Syntax Tree ## Parsing Flow -1. **Input**: NQL files containing schemas, queries, and migrations +1. **Input**: SensibleQL files containing schemas, queries, and migrations 2. **Lexing**: Pest tokenizes input according to `grammar.pest` rules 3. **AST Construction**: - Schemas parsed first (establishing type definitions) diff --git a/sensibledb-explorer/icons/128x128.png b/sensibledb-explorer/icons/128x128.png index 2243a781..ff158536 100644 Binary files a/sensibledb-explorer/icons/128x128.png and b/sensibledb-explorer/icons/128x128.png differ diff --git a/sensibledb-explorer/icons/128x128@2x.png b/sensibledb-explorer/icons/128x128@2x.png index dbd4d907..3e087d83 100644 Binary files a/sensibledb-explorer/icons/128x128@2x.png and b/sensibledb-explorer/icons/128x128@2x.png differ diff --git a/sensibledb-explorer/icons/32x32.png b/sensibledb-explorer/icons/32x32.png index 3731e51a..5098faf4 100644 Binary files a/sensibledb-explorer/icons/32x32.png and b/sensibledb-explorer/icons/32x32.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_1024x1024.png b/sensibledb-explorer/icons/app.iconset/icon_1024x1024.png new file mode 100644 index 00000000..14853cbe Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_1024x1024.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_128x128.png b/sensibledb-explorer/icons/app.iconset/icon_128x128.png new file mode 100644 index 00000000..ff158536 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_128x128.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_128x128@2x.png b/sensibledb-explorer/icons/app.iconset/icon_128x128@2x.png new file mode 100644 index 00000000..3e087d83 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_128x128@2x.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_16x16.png b/sensibledb-explorer/icons/app.iconset/icon_16x16.png new file mode 100644 index 00000000..e4429d76 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_16x16.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_16x16@2x.png b/sensibledb-explorer/icons/app.iconset/icon_16x16@2x.png new file mode 100644 index 00000000..5098faf4 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_16x16@2x.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_256x256.png b/sensibledb-explorer/icons/app.iconset/icon_256x256.png new file mode 100644 index 00000000..3e087d83 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_256x256.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_256x256@2x.png b/sensibledb-explorer/icons/app.iconset/icon_256x256@2x.png new file mode 100644 index 00000000..3ade7091 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_256x256@2x.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_32x32.png b/sensibledb-explorer/icons/app.iconset/icon_32x32.png new file mode 100644 index 00000000..5098faf4 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_32x32.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_32x32@2x.png b/sensibledb-explorer/icons/app.iconset/icon_32x32@2x.png new file mode 100644 index 00000000..2e77b984 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_32x32@2x.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_512x512.png b/sensibledb-explorer/icons/app.iconset/icon_512x512.png new file mode 100644 index 00000000..3ade7091 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_512x512.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_512x512@2x.png b/sensibledb-explorer/icons/app.iconset/icon_512x512@2x.png new file mode 100644 index 00000000..14853cbe Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_512x512@2x.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_64x64.png b/sensibledb-explorer/icons/app.iconset/icon_64x64.png new file mode 100644 index 00000000..2e77b984 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_64x64.png differ diff --git a/sensibledb-explorer/icons/app.iconset/icon_64x64@2x.png b/sensibledb-explorer/icons/app.iconset/icon_64x64@2x.png new file mode 100644 index 00000000..ff158536 Binary files /dev/null and b/sensibledb-explorer/icons/app.iconset/icon_64x64@2x.png differ diff --git a/sensibledb-explorer/icons/icon.icns b/sensibledb-explorer/icons/icon.icns index dbd4d907..cb2a23dc 100644 Binary files a/sensibledb-explorer/icons/icon.icns and b/sensibledb-explorer/icons/icon.icns differ diff --git a/sensibledb-explorer/icons/icon.ico b/sensibledb-explorer/icons/icon.ico new file mode 100644 index 00000000..c535f9ed Binary files /dev/null and b/sensibledb-explorer/icons/icon.ico differ diff --git a/sensibledb-explorer/src/commands/database.rs b/sensibledb-explorer/src/commands/database.rs index e3a217a7..69f22aa9 100644 --- a/sensibledb-explorer/src/commands/database.rs +++ b/sensibledb-explorer/src/commands/database.rs @@ -56,13 +56,25 @@ pub fn db_close(state: tauri::State, name: String) -> Result) -> Result, String> { let dbs = state.databases.lock().map_err(|e| e.to_string())?; let keys: Vec = dbs.keys().cloned().collect(); - let msg = format!("[DB_LIST] Returning {} databases: {:?} -", keys.len(), keys); + let msg = format!( + "[DB_LIST] Returning {} databases: {:?} +", + keys.len(), + keys + ); eprint!("{}", msg); - std::fs::write("/tmp/sensibledb-explorer.log", format!("{} + std::fs::write( + "/tmp/sensibledb-explorer.log", + format!( + "{} [DB_LIST] Returning {} databases: {:?} -", - std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), keys.len(), keys)).ok(); +", + std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), + keys.len(), + keys + ), + ) + .ok(); Ok(keys) } @@ -112,10 +124,13 @@ pub fn db_create_demo_internal(state: &AppState) -> Result<(), String> { #[tauri::command] pub fn db_create_demo(state: tauri::State) -> Result { - db_create_demo_internal(&state).map(|_| "Demo databases ready: health-patterns, project-management".to_string()) + db_create_demo_internal(&state) + .map(|_| "Demo databases ready: health-patterns, project-management".to_string()) } -fn populate_health_patterns(db: &sensibledb_db::embedded::database::Database) -> Result<(), String> { +fn populate_health_patterns( + db: &sensibledb_db::embedded::database::Database, +) -> Result<(), String> { db.put_node(Node { id: 1, label: "Person:Alex".to_string(), @@ -291,64 +306,236 @@ fn populate_health_patterns(db: &sensibledb_db::embedded::database::Database) -> #[tauri::command] pub fn log_error(msg: String) { eprintln!("[FRONTEND_ERROR] {}", msg); - let _ = std::fs::write("/tmp/sensibledb-explorer.log", format!("{} + let _ = std::fs::write( + "/tmp/sensibledb-explorer.log", + format!( + "{} [FRONTEND_ERROR] {} -", - std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), msg)); +", + std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), + msg + ), + ); } -fn populate_project_management(db: &sensibledb_db::embedded::database::Database) -> Result<(), String> { +fn populate_project_management( + db: &sensibledb_db::embedded::database::Database, +) -> Result<(), String> { // Team Members - db.put_node(Node { id: 1, label: "Person:Alice".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 2, label: "Person:Bob".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 3, label: "Person:Carol".to_string() }).map_err(|e| e.to_string())?; + db.put_node(Node { + id: 1, + label: "Person:Alice".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 2, + label: "Person:Bob".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 3, + label: "Person:Carol".to_string(), + }) + .map_err(|e| e.to_string())?; // Projects - db.put_node(Node { id: 10, label: "Project:WebsiteRedesign".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 11, label: "Project:MobileApp".to_string() }).map_err(|e| e.to_string())?; + db.put_node(Node { + id: 10, + label: "Project:WebsiteRedesign".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 11, + label: "Project:MobileApp".to_string(), + }) + .map_err(|e| e.to_string())?; // Tasks - db.put_node(Node { id: 20, label: "Task:DesignMockups".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 21, label: "Task:FrontendDev".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 22, label: "Task:BackendAPI".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 23, label: "Task:Testing".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 24, label: "Task:Deployment".to_string() }).map_err(|e| e.to_string())?; + db.put_node(Node { + id: 20, + label: "Task:DesignMockups".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 21, + label: "Task:FrontendDev".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 22, + label: "Task:BackendAPI".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 23, + label: "Task:Testing".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 24, + label: "Task:Deployment".to_string(), + }) + .map_err(|e| e.to_string())?; // Tools - db.put_node(Node { id: 30, label: "Tool:Figma".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 31, label: "Tool:GitHub".to_string() }).map_err(|e| e.to_string())?; - db.put_node(Node { id: 32, label: "Tool:AWS".to_string() }).map_err(|e| e.to_string())?; + db.put_node(Node { + id: 30, + label: "Tool:Figma".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 31, + label: "Tool:GitHub".to_string(), + }) + .map_err(|e| e.to_string())?; + db.put_node(Node { + id: 32, + label: "Tool:AWS".to_string(), + }) + .map_err(|e| e.to_string())?; // Assignments: Person -> Project - db.put_edge(Edge { id: 100, label: "ASSIGNED_TO".to_string(), from: 1, to: 10 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 101, label: "ASSIGNED_TO".to_string(), from: 2, to: 10 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 102, label: "ASSIGNED_TO".to_string(), from: 3, to: 11 }).map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 100, + label: "ASSIGNED_TO".to_string(), + from: 1, + to: 10, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 101, + label: "ASSIGNED_TO".to_string(), + from: 2, + to: 10, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 102, + label: "ASSIGNED_TO".to_string(), + from: 3, + to: 11, + }) + .map_err(|e| e.to_string())?; // Tasks belong to projects - db.put_edge(Edge { id: 110, label: "PART_OF".to_string(), from: 20, to: 10 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 111, label: "PART_OF".to_string(), from: 21, to: 10 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 112, label: "PART_OF".to_string(), from: 22, to: 11 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 113, label: "PART_OF".to_string(), from: 23, to: 11 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 114, label: "PART_OF".to_string(), from: 24, to: 11 }).map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 110, + label: "PART_OF".to_string(), + from: 20, + to: 10, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 111, + label: "PART_OF".to_string(), + from: 21, + to: 10, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 112, + label: "PART_OF".to_string(), + from: 22, + to: 11, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 113, + label: "PART_OF".to_string(), + from: 23, + to: 11, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 114, + label: "PART_OF".to_string(), + from: 24, + to: 11, + }) + .map_err(|e| e.to_string())?; // Task dependencies - db.put_edge(Edge { id: 120, label: "BLOCKS".to_string(), from: 20, to: 21 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 121, label: "BLOCKS".to_string(), from: 21, to: 23 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 122, label: "BLOCKS".to_string(), from: 22, to: 23 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 123, label: "BLOCKS".to_string(), from: 23, to: 24 }).map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 120, + label: "BLOCKS".to_string(), + from: 20, + to: 21, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 121, + label: "BLOCKS".to_string(), + from: 21, + to: 23, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 122, + label: "BLOCKS".to_string(), + from: 22, + to: 23, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 123, + label: "BLOCKS".to_string(), + from: 23, + to: 24, + }) + .map_err(|e| e.to_string())?; // Tasks use tools - db.put_edge(Edge { id: 130, label: "USES".to_string(), from: 20, to: 30 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 131, label: "USES".to_string(), from: 21, to: 31 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 132, label: "USES".to_string(), from: 22, to: 31 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 133, label: "USES".to_string(), from: 24, to: 32 }).map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 130, + label: "USES".to_string(), + from: 20, + to: 30, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 131, + label: "USES".to_string(), + from: 21, + to: 31, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 132, + label: "USES".to_string(), + from: 22, + to: 31, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 133, + label: "USES".to_string(), + from: 24, + to: 32, + }) + .map_err(|e| e.to_string())?; // Person owns tasks - db.put_edge(Edge { id: 140, label: "OWNS".to_string(), from: 1, to: 20 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 141, label: "OWNS".to_string(), from: 2, to: 21 }).map_err(|e| e.to_string())?; - db.put_edge(Edge { id: 142, label: "OWNS".to_string(), from: 3, to: 22 }).map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 140, + label: "OWNS".to_string(), + from: 1, + to: 20, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 141, + label: "OWNS".to_string(), + from: 2, + to: 21, + }) + .map_err(|e| e.to_string())?; + db.put_edge(Edge { + id: 142, + label: "OWNS".to_string(), + from: 3, + to: 22, + }) + .map_err(|e| e.to_string())?; Ok(()) } - - diff --git a/sensibledb-explorer/src/commands/mod.rs b/sensibledb-explorer/src/commands/mod.rs index 7d6b6af7..f17eaac0 100644 --- a/sensibledb-explorer/src/commands/mod.rs +++ b/sensibledb-explorer/src/commands/mod.rs @@ -1,11 +1,11 @@ pub mod database; pub mod edges; pub mod nodes; -pub mod nql; pub mod schema; +pub mod sensibleql; pub use database::*; pub use edges::*; pub use nodes::*; -pub use nql::*; pub use schema::*; +pub use sensibleql::*; diff --git a/sensibledb-explorer/src/commands/nodes.rs b/sensibledb-explorer/src/commands/nodes.rs index bbf2d99b..03d2bc3a 100644 --- a/sensibledb-explorer/src/commands/nodes.rs +++ b/sensibledb-explorer/src/commands/nodes.rs @@ -87,25 +87,51 @@ pub fn node_delete( #[tauri::command] pub fn node_list(state: tauri::State, db_name: String) -> Result, String> { - let log_msg = format!("[NODE_LIST] Called with db_name={} -", db_name); + let log_msg = format!( + "[NODE_LIST] Called with db_name={} +", + db_name + ); eprint!("{}", log_msg); - let _ = std::fs::write("/tmp/sensibledb-explorer.log", format!("{} -{}", - std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), log_msg)); + let _ = std::fs::write( + "/tmp/sensibledb-explorer.log", + format!( + "{} +{}", + std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), + log_msg + ), + ); let dbs = state.databases.lock().map_err(|e| e.to_string())?; - eprintln!("[NODE_LIST] Available DBs: {:?}", dbs.keys().collect::>()); - let db = dbs - .get(&db_name) - .ok_or_else(|| format!("Database '{}' not found. Available: {:?}", db_name, dbs.keys().collect::>()))?; + eprintln!( + "[NODE_LIST] Available DBs: {:?}", + dbs.keys().collect::>() + ); + let db = dbs.get(&db_name).ok_or_else(|| { + format!( + "Database '{}' not found. Available: {:?}", + db_name, + dbs.keys().collect::>() + ) + })?; let tx = db.read_transaction().map_err(|e| e.to_string())?; let nodes = tx.scan_nodes().map_err(|e| e.to_string())?; - let log_msg2 = format!("[NODE_LIST] db={} found {} nodes -", db_name, nodes.len()); + let log_msg2 = format!( + "[NODE_LIST] db={} found {} nodes +", + db_name, + nodes.len() + ); eprint!("{}", log_msg2); - let _ = std::fs::write("/tmp/sensibledb-explorer.log", format!("{} -{}", - std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), log_msg2)); + let _ = std::fs::write( + "/tmp/sensibledb-explorer.log", + format!( + "{} +{}", + std::fs::read_to_string("/tmp/sensibledb-explorer.log").unwrap_or_default(), + log_msg2 + ), + ); Ok(nodes .into_iter() .map(|n| NodeDto { diff --git a/sensibledb-explorer/src/commands/nql.rs b/sensibledb-explorer/src/commands/sensibleql.rs similarity index 59% rename from sensibledb-explorer/src/commands/nql.rs rename to sensibledb-explorer/src/commands/sensibleql.rs index 2a725038..c90ac362 100644 --- a/sensibledb-explorer/src/commands/nql.rs +++ b/sensibledb-explorer/src/commands/sensibleql.rs @@ -3,13 +3,13 @@ use sensibledb_db::embedded::transaction::{Edge, Node, ReadTransaction}; use serde::{Deserialize, Serialize}; #[derive(Deserialize)] -pub struct NqlRequest { +pub struct SensibleqlRequest { pub db_name: String, pub query: String, } #[derive(Serialize, Deserialize)] -pub struct NqlResult { +pub struct SensibleqlResult { pub success: bool, pub message: String, pub data: Option, @@ -37,11 +37,11 @@ struct QResult { } #[tauri::command(rename_all = "camelCase")] -pub fn nql_execute( +pub fn sensibleql_execute( state: tauri::State, db_name: String, query: String, -) -> Result { +) -> Result { let dbs = state.databases.lock().map_err(|e| e.to_string())?; let db = dbs .get(&db_name) @@ -61,49 +61,105 @@ pub fn nql_execute( do_count(&q, &all_nodes, &all_edges)? } else { QResult { - nodes: all_nodes.iter().map(|n| QNode { id: n.id, label: n.label.clone() }).collect(), - edges: all_edges.iter().map(|e| QEdge { id: e.id, label: e.label.clone(), from: e.from, to: e.to }).collect(), + nodes: all_nodes + .iter() + .map(|n| QNode { + id: n.id, + label: n.label.clone(), + }) + .collect(), + edges: all_edges + .iter() + .map(|e| QEdge { + id: e.id, + label: e.label.clone(), + from: e.from, + to: e.to, + }) + .collect(), count: all_nodes.len(), } }; - Ok(NqlResult { + Ok(SensibleqlResult { success: true, - message: format!("Query returned {} nodes and {} edges", result.nodes.len(), result.edges.len()), + message: format!( + "Query returned {} nodes and {} edges", + result.nodes.len(), + result.edges.len() + ), data: Some(serde_json::to_value(&result).map_err(|e| e.to_string())?), }) } fn do_match(q: &str, nodes: &[Node], edges: &[Edge]) -> Result { let label = pick_label(q); - let matched: Vec = nodes.iter() - .filter(|n| label.as_ref().map_or(true, |l| n.label.to_lowercase().contains(l))) - .map(|n| QNode { id: n.id, label: n.label.clone() }) + let matched: Vec = nodes + .iter() + .filter(|n| { + label + .as_ref() + .map_or(true, |l| n.label.to_lowercase().contains(l)) + }) + .map(|n| QNode { + id: n.id, + label: n.label.clone(), + }) .collect(); let matched_edges: Vec = if q.contains(")-[") || q.contains("]->") { let el = pick_edge_label(q); - edges.iter() + edges + .iter() .filter(|e| { let nm = matched.iter().any(|n| n.id == e.from || n.id == e.to); - el.as_ref().map_or(nm, |x| e.label.to_lowercase().contains(x) && nm) + el.as_ref() + .map_or(nm, |x| e.label.to_lowercase().contains(x) && nm) + }) + .map(|e| QEdge { + id: e.id, + label: e.label.clone(), + from: e.from, + to: e.to, }) - .map(|e| QEdge { id: e.id, label: e.label.clone(), from: e.from, to: e.to }) .collect() - } else { vec![] }; - Ok(QResult { nodes: matched.clone(), edges: matched_edges, count: matched.len() }) + } else { + vec![] + }; + Ok(QResult { + nodes: matched.clone(), + edges: matched_edges, + count: matched.len(), + }) } fn do_get(q: &str, nodes: &[Node], edges: &[Edge]) -> Result { let term = pick_search(q); - let matched: Vec = nodes.iter() - .filter(|n| term.as_ref().map_or(true, |s| n.label.to_lowercase().contains(s))) - .map(|n| QNode { id: n.id, label: n.label.clone() }) + let matched: Vec = nodes + .iter() + .filter(|n| { + term.as_ref() + .map_or(true, |s| n.label.to_lowercase().contains(s)) + }) + .map(|n| QNode { + id: n.id, + label: n.label.clone(), + }) .collect(); - let matched_edges: Vec = edges.iter() + let matched_edges: Vec = edges + .iter() .filter(|e| matched.iter().any(|n| n.id == e.from || n.id == e.to)) - .map(|e| QEdge { id: e.id, label: e.label.clone(), from: e.from, to: e.to }) + .map(|e| QEdge { + id: e.id, + label: e.label.clone(), + from: e.from, + to: e.to, + }) .collect(); - Ok(QResult { nodes: matched.clone(), edges: matched_edges, count: matched.len() }) + Ok(QResult { + nodes: matched.clone(), + edges: matched_edges, + count: matched.len(), + }) } fn do_count(q: &str, nodes: &[Node], edges: &[Edge]) -> Result { @@ -111,9 +167,20 @@ fn do_count(q: &str, nodes: &[Node], edges: &[Edge]) -> Result let count = if q.contains("edge") || q.contains("relationship") { edges.len() } else { - nodes.iter().filter(|n| label.as_ref().map_or(true, |l| n.label.to_lowercase().contains(l))).count() + nodes + .iter() + .filter(|n| { + label + .as_ref() + .map_or(true, |l| n.label.to_lowercase().contains(l)) + }) + .count() }; - Ok(QResult { nodes: vec![], edges: vec![], count }) + Ok(QResult { + nodes: vec![], + edges: vec![], + count, + }) } fn pick_label(q: &str) -> Option { @@ -127,7 +194,9 @@ fn pick_label(q: &str) -> Option { // Node pattern: single letter variable like (n:Label) if between.len() <= 2 && between.chars().all(|c| c.is_alphabetic()) { let after = &q[i + 1..]; - let end = after.find(|c: char| !c.is_alphanumeric() && c != '_').unwrap_or(after.len()); + let end = after + .find(|c: char| !c.is_alphanumeric() && c != '_') + .unwrap_or(after.len()); if end > 0 { return Some(after[..end].to_string()); } @@ -143,7 +212,11 @@ fn pick_edge_label(q: &str) -> Option { let colon = rest.find(':')?; let after = &rest[colon + 1..]; let end = after.find(']')?; - if end > 0 { Some(after[..end].to_string()) } else { None } + if end > 0 { + Some(after[..end].to_string()) + } else { + None + } } fn pick_search(q: &str) -> Option { diff --git a/sensibledb-explorer/src/frontend/src/App.css b/sensibledb-explorer/src/frontend/src/App.css index 641b491f..b4ff01f5 100644 --- a/sensibledb-explorer/src/frontend/src/App.css +++ b/sensibledb-explorer/src/frontend/src/App.css @@ -13,8 +13,9 @@ border-bottom: 1px solid var(--border); display: flex; align-items: center; + justify-content: center; padding: 0 var(--space-lg); - gap: var(--space-lg); + position: relative; flex-shrink: 0; } @@ -22,6 +23,9 @@ display: flex; align-items: center; gap: var(--space-sm); + position: absolute; + left: 50%; + transform: translateX(-50%); } .header-logo { @@ -35,16 +39,19 @@ } .header-accent { - color: #6366f1; + color: var(--accent)6366f1; } .header-db-selector { - flex: 1; + position: absolute; + left: var(--space-lg); display: flex; align-items: center; } .header-actions { + position: absolute; + right: var(--space-lg); display: flex; gap: var(--space-sm); } diff --git a/sensibledb-explorer/src/frontend/src/App.tsx b/sensibledb-explorer/src/frontend/src/App.tsx index cd4f857c..f3743213 100644 --- a/sensibledb-explorer/src/frontend/src/App.tsx +++ b/sensibledb-explorer/src/frontend/src/App.tsx @@ -5,7 +5,7 @@ import GraphView from "./components/graph/GraphView"; import NodeList from "./components/entities/NodeList"; import EdgeList from "./components/entities/EdgeList"; import SchemaBrowser from "./components/sidebar/SchemaBrowser"; -import NqlEditor from "./components/editor/NqlEditor"; +import SensibleQLEditor from "./components/editor/SensibleQLEditor"; import HomeView from "./components/home/HomeView"; import ChatView from "./components/chat/ChatView"; import ReportView from "./components/report/ReportView"; @@ -61,7 +61,7 @@ const App: Component = () => { else if (e.key === "5") setActiveView("nodes"); else if (e.key === "6") setActiveView("edges"); else if (e.key === "7") setActiveView("schema"); - else if (e.key === "8") setActiveView("nql"); + else if (e.key === "8") setActiveView("sensibleql"); else if (e.key === "Escape") { if (selectedNode()) setSelectedNode(null); else setActiveView("home"); @@ -135,8 +135,8 @@ const App: Component = () => { - - + +