{"id":50428223,"url":"https://github.com/goldenhub/mongeesy","last_synced_at":"2026-05-31T12:01:30.186Z","repository":{"id":356841216,"uuid":"1234276585","full_name":"Goldenhub/mongeesy","owner":"Goldenhub","description":"An interactive, in-browser MongoDB tutorial. Write real MongoDB queries against real data and get instant feedback — no setup, no signup, no cloud.","archived":false,"fork":false,"pushed_at":"2026-05-11T16:14:06.000Z","size":640,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T17:33:09.677Z","etag":null,"topics":["database","learning","mongodb"],"latest_commit_sha":null,"homepage":"https://mongeesy.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Goldenhub.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"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-05-10T01:11:32.000Z","updated_at":"2026-05-11T16:15:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Goldenhub/mongeesy","commit_stats":null,"previous_names":["goldenhub/mongodb-easy","goldenhub/mongeesy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Goldenhub/mongeesy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Goldenhub%2Fmongeesy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Goldenhub%2Fmongeesy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Goldenhub%2Fmongeesy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Goldenhub%2Fmongeesy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Goldenhub","download_url":"https://codeload.github.com/Goldenhub/mongeesy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Goldenhub%2Fmongeesy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33730241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["database","learning","mongodb"],"created_at":"2026-05-31T12:01:28.794Z","updated_at":"2026-05-31T12:01:30.180Z","avatar_url":"https://github.com/Goldenhub.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mongeesy\n\nAn interactive, in-browser MongoDB playground. Write real MongoDB queries against real data and get instant feedback — no setup, no signup, no cloud.\n\n## Features\n\n- **Real MongoDB queries** — Type real `find()`, `aggregate()`, `sort()`, `group()`, `lookup()` syntax. A custom query engine runs it all in your browser.\n- **Immediate feedback** — See your result side-by-side with the expected output. Know instantly if you got it right.\n- **32 progressive lessons** — Start with `find()` and work up to `$bucket`, `$facet`, and `$lookup`. Five modules covering reading, aggregation, writes, advanced queries, and analytical patterns.\n- **Hints that guide** — Progressive hints for each lesson that point you in the right direction without giving away the answer.\n- **No signup, no cost** — Everything runs client-side. No account, no email, no credit card.\n- **Progress that persists** — Completed lessons, last query, and attempt counts are saved to localStorage automatically.\n- **Analytics** — Optional PostHog integration tracks lesson completions, query attempts, hint usage, and drop-off to help improve the curriculum.\n\n## Tech Stack\n\n| Tool                | Purpose                      |\n| ------------------- | ---------------------------- |\n| **React 19**        | UI framework                 |\n| **Vite 8**          | Dev server and bundler       |\n| **Tailwind CSS v4** | Utility-first styling        |\n| **React Router v7** | Client-side routing          |\n| **Monaco Editor**   | In-browser code editor       |\n| **PostHog**         | Product analytics (optional) |\n| **canvas-confetti** | Celebration animations       |\n| **Vitest**          | Unit testing                 |\n| **ESLint**          | Linting                      |\n\n## Getting Started\n\n```bash\nnpm install\nnpm run dev\n```\n\nOpen the local URL shown in the terminal (usually `http://localhost:5173`).\n\n### Environment Variables\n\n| Variable                   | Required | Default                    | Description                           |\n| -------------------------- | -------- | -------------------------- | ------------------------------------- |\n| `VITE_PUBLIC_POSTHOG_KEY`  | No       | —                          | PostHog project API key for analytics |\n| `VITE_PUBLIC_POSTHOG_HOST` | No       | `https://us.i.posthog.com` | PostHog instance host                 |\n\nAnalytics is a no-op when the key is not set, so you can develop without it.\n\n\u003e **Note:** `VITE_POSTHOG_KEY` / `VITE_POSTHOG_HOST` (without `PUBLIC_`) are also accepted for compatibility.\n\n## Scripts\n\n| Command           | Description                          |\n| ----------------- | ------------------------------------ |\n| `npm run dev`     | Start the Vite dev server with HMR   |\n| `npm run build`   | Build for production into `dist/`    |\n| `npm run preview` | Preview the production build locally |\n| `npm run lint`    | Run ESLint across all source files   |\n| `npm run test`    | Run unit tests with Vitest           |\n\n## Project Structure\n\n```\nsrc/\n├── components/       # React components (Sidebar, MainPanel, QueryEditor, etc.)\n├── data/             # Sample MongoDB collections (books, products, authors, etc.)\n├── engine/           # Custom MongoDB query engine\n│   ├── query-engine.js     # Database class — high-level operations\n│   ├── pipeline-engine.js  # Aggregation pipeline executor\n│   ├── mongosh-parser.js   # MongoDB shell syntax parser\n│   ├── operators.js        # Query and expression operators\n│   └── collection.js       # Collection class with CRUD operations\n├── hooks/            # Custom React hooks (useProgress)\n├── lib/              # Analytics helpers (PostHog)\n├── lessons/          # 32 lesson files (one per concept)\n├── pages/            # LandingPage and LearnPage\n└── utils/            # Helpers (modules, comparison, table formatting)\n```\n\n## How It Works\n\n1. **Read the explanation** — Each lesson introduces one concept with a clear example.\n2. **Write the query** — Type the MongoDB query from scratch in the Monaco editor.\n3. **Run and compare** — Press `Cmd+Enter` (or `Ctrl+Enter`) to execute. See your result next to the expected output.\n\nThe query engine parses MongoDB shell syntax, executes against in-memory collections, and compares results using deep equality. All data is pre-loaded sample datasets — no network requests needed.\n\n## Architecture\n\nThe custom MongoDB query engine is split into five modules in `src/engine/`, each with a single responsibility:\n\n### `mongosh-parser.js`\nTokenizes MongoDB shell syntax (`db.books.find({ ... }).sort({ ... })`) into structured command objects. Handles nested parentheses, string literals, regex patterns, and chained methods. Uses `new Function` to evaluate raw JS argument strings into real objects.\n\n### `operators.js`\nEvaluates query filters against documents. Supports all major operators: `$gt`, `$in`, `$regex`, `$and`/`$or`, `$expr`, `$elemMatch`, `$where`. Resolves dotted field paths like `address.city`.\n\n### `collection.js`\nIn-memory document array with CRUD methods: `find`, `insertOne`, `updateMany`, `deleteOne`, `distinct`, `aggregate`. Applies projections, update operators (`$set`, `$inc`, `$push`, `$pull`), and auto-generates sequential numeric `_id` values.\n\n### `pipeline-engine.js`\nAggregation pipeline executor. Stages (`$match`, `$group`, `$lookup`, `$bucket`, `$facet`) are applied sequentially against cloned documents. Expression resolution handles computed fields, conditionals (`$cond`), arithmetic, and accumulators (`$sum`, `$avg`, `$push`).\n\n### `query-engine.js`\nTop-level `Database` class. On `execute(query)`:\n1. Parses the query string via `mongosh-parser.js`\n2. Evaluates arguments to real JS values\n3. Dispatches to the right `Collection` method\n4. Applies chained cursor methods (`.sort()`, `.limit()`, `.count()`)\n5. Returns `{ result, collection, method }`\n\nAll data lives in memory — no network, no server. Documents are deep-cloned to prevent mutation. The `$lookup` stage references other collections via a shared collections map.\n\n## Analytics\n\nPostHog analytics is built in but disabled by default. Set `VITE_PUBLIC_POSTHOG_KEY` in `.env` to enable.\n\nThe following events are tracked when analytics is active:\n\n| Event                   | When                                                            |\n| ----------------------- | --------------------------------------------------------------- |\n| `$pageview`             | Every page navigation                                           |\n| `cta_clicked`           | Click on any \"Start learning\" / CTA button on the landing page  |\n| `lesson_started`        | User opens a lesson                                             |\n| `query_run`             | User executes a query (includes `matched` and `total_attempts`) |\n| `lesson_completed`      | User gets the correct answer                                    |\n| `module_completed`      | All lessons in a module are finished                            |\n| `all_lessons_completed` | All 32 lessons are finished                                     |\n| `query_error`           | Query throws a parse or execution error                         |\n| `query_reset`           | User presses the Reset button                                   |\n| `hint_viewed`           | User opens a hint or navigates between hints                    |\n| `playground_opened`     | User enters the free-form playground mode                       |\n| `collections_panel_opened` | User opens the collections panel on mobile                   |\n| `result_view_toggled`   | User switches between Table and JSON result view (includes `view`) |\n\nNo personal data is collected. Events are associated with a random anonymous ID stored in localStorage.\n\n## Adding Lessons\n\nEach lesson is a standalone JSX file in `src/lessons/` that exports a lesson object. Here's the pattern:\n\n```js\n// src/lessons/33-new-concept.jsx\nimport books from '../data/books.js'\n\nconst lesson = {\n  id: 33,                           // unique, sequential ID\n  title: '$newOperator - Concept',   // short title\n  module: 'Module 3: Modifying Data', // module name (used for display)\n  description: 'One-line summary',\n\n  explanation: (                     // JSX rendered as the main lesson content\n    \u003c\u003e\n      \u003cp\u003eExplanation of the concept with \u003ccode\u003ecode examples\u003c/code\u003e.\u003c/p\u003e\n    \u003c/\u003e\n  ),\n\n  // Optional — expandable detail boxes:\n  howItWorks: \u003cp\u003eDeep dive into mechanics.\u003c/p\u003e,\n  realWorldUse: \u003cp\u003eProduction use case.\u003c/p\u003e,\n  commonMistakes: \u003cp\u003ePitfalls to avoid.\u003c/p\u003e,\n  syntaxBreakdown: {                 // Shows a labeled code breakdown\n    query: 'db.books.find({...})',\n    parts: [\n      { label: 'find()', description: 'What this part does' },\n    ],\n  },\n  dataFlow: ['Collection', '$stage1', '$stage2', 'Result'],\n\n  task: 'Description of what the user should write.',\n\n  defaultQuery: 'db.books.find()',   // pre-filled query that passes the lesson\n\n  collections: { books },            // collection name → data mapping\n\n  expectedResult: [{ ... }],         // array of documents the correct query returns\n\n  hints: [                           // progressive hints, shown one at a time\n    'First hint — vague pointer',\n    'Second hint — more specific',\n    'Third hint — nearly the answer',\n  ],\n}\n\nexport default lesson\n```\n\n### Registering a new lesson\n\n1. Create the lesson file in `src/lessons/` (e.g. `32-new-concept.jsx`).\n2. Import and add it to the array in `src/lessons/index.js`.\n3. If the lesson starts a new module, add its starting lesson ID to `MODULE_NAMES` in `src/utils/modules.js`.\n4. Run `npm run test` to verify the default query passes.\n\n### Lesson fields reference\n\n| Field             | Required | Description                                                  |\n| ----------------- | -------- | ------------------------------------------------------------ |\n| `id`              | yes      | Unique integer. Must match the order in the `lessons` array. |\n| `title`           | yes      | Short display name.                                          |\n| `module`          | yes      | Module name shown in the header.                             |\n| `description`     | yes      | One-line summary for meta/SEO.                               |\n| `explanation`     | yes      | JSX — the main teaching content.                             |\n| `task`            | yes      | What the user needs to do.                                   |\n| `defaultQuery`    | yes      | The query string that passes the lesson. Used by tests.      |\n| `collections`     | yes      | Object mapping collection names to their data arrays.        |\n| `expectedResult`  | yes      | Array of documents the correct query produces.               |\n| `hints`           | no       | Array of progressive hint strings.                           |\n| `howItWorks`      | no       | JSX — expandable \"How it works\" box.                         |\n| `realWorldUse`    | no       | JSX — expandable \"Real-world use\" box.                       |\n| `commonMistakes`  | no       | JSX — expandable \"Common mistakes\" box.                      |\n| `syntaxBreakdown` | no       | Object with `query` string and `parts` array.                |\n| `dataFlow`        | no       | Array of stage name strings for pipeline visualization.      |\n\n## Testing\n\n```bash\nnpm run test\n```\n\nThe test suite auto-discovers all lessons in `src/lessons/index.js`. For each lesson it:\n\n1. Creates a fresh in-memory database with the lesson's collections.\n2. Executes the lesson's `defaultQuery`.\n3. Compares the result against `expectedResult` using deep equality.\n\nWhen adding a new lesson, write the `defaultQuery` and `expectedResult` first, then run `npm run test` to confirm they match before building the UI content.\n\n### Test file\n\n`src/lessons/lessons.test.js` — a single Vitest test file that iterates over every lesson:\n\n```js\nlessons.forEach((lesson) =\u003e {\n  it(`lesson ${lesson.id}: ${lesson.title}`, () =\u003e {\n    const db = new Database(lesson.collections);\n    const { result } = db.execute(lesson.defaultQuery);\n    const resultArray = Array.isArray(result) ? result : [result];\n    const match = compareResults(resultArray, lesson.expectedResult);\n    if (!match) {\n      expect(resultArray).toEqual(lesson.expectedResult);\n    }\n  });\n});\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoldenhub%2Fmongeesy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoldenhub%2Fmongeesy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoldenhub%2Fmongeesy/lists"}