├── edgedb.toml ├── mcu.dump ├── .gitignore ├── script.ts ├── dbschema ├── default.esdl └── migrations │ └── 00001.edgeql ├── tsconfig.json ├── package.json ├── README.md └── seed.ts /edgedb.toml: -------------------------------------------------------------------------------- 1 | [edgedb] 2 | server-version = "2.0" 3 | -------------------------------------------------------------------------------- /mcu.dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geldata/mcu-sandbox/HEAD/mcu.dump -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dbschema/edgeql-js 2 | node_modules 3 | .DS_Store 4 | .next 5 | .vscode 6 | dist -------------------------------------------------------------------------------- /script.ts: -------------------------------------------------------------------------------- 1 | import {createClient} from 'edgedb'; 2 | import e from './dbschema/edgeql-js'; 3 | 4 | const client = createClient(); 5 | 6 | async function run() { 7 | console.log(`run`); 8 | const query = e.select(e.Person, (p) => ({ 9 | id: true, 10 | movies: p[' str; 7 | multi link entries -> Content; 8 | } 9 | 10 | abstract type Content { 11 | required property title -> str { constraint exclusive; }; 12 | multi link actors -> Person { 13 | property character_name -> str; 14 | }; 15 | } 16 | 17 | type Movie extending Content { 18 | link director -> Person; 19 | property release_year -> int64; 20 | } 21 | 22 | type TVShow extending Content { 23 | property num_seasons -> int64; 24 | } 25 | 26 | type Person { 27 | required property name -> str; 28 | multi link acted_in := .=14.0.0" 21 | }, 22 | "devDependencies": { 23 | "@tsconfig/node14": "^1.0.1", 24 | "@types/node": "^17.0.16", 25 | "@typescript-eslint/eslint-plugin": "^5.30.5", 26 | "@typescript-eslint/parser": "^5.30.5", 27 | "eslint": "^8.19.0", 28 | "eslint-config-prettier": "^8.5.0", 29 | "eslint-plugin-import": "^2.26.0", 30 | "eslint-plugin-simple-import-sort": "^7.0.0", 31 | "eslint-plugin-unused-imports": "^2.0.0", 32 | "nodemon": "^2.0.19", 33 | "prettier": "^2.7.1", 34 | "tsx": "^3.8.0", 35 | "typescript": "^4.6.2" 36 | } 37 | } -------------------------------------------------------------------------------- /dbschema/migrations/00001.edgeql: -------------------------------------------------------------------------------- 1 | CREATE MIGRATION m17cfxwywkdu6aula4a4ui46ieje7irwxr27b7kuyqacmjjefaycxq 2 | ONTO initial 3 | { 4 | CREATE EXTENSION graphql VERSION '1.0'; 5 | CREATE ABSTRACT TYPE default::Content { 6 | CREATE REQUIRED PROPERTY title -> std::str { 7 | CREATE CONSTRAINT std::exclusive; 8 | }; 9 | }; 10 | CREATE TYPE default::Movie EXTENDING default::Content { 11 | CREATE PROPERTY release_year -> std::int64; 12 | }; 13 | CREATE TYPE default::TVShow EXTENDING default::Content { 14 | CREATE PROPERTY num_seasons -> std::int64; 15 | }; 16 | CREATE TYPE default::Person { 17 | CREATE REQUIRED PROPERTY name -> std::str; 18 | }; 19 | ALTER TYPE default::Content { 20 | CREATE MULTI LINK actors -> default::Person { 21 | CREATE PROPERTY character_name -> std::str; 22 | }; 23 | }; 24 | ALTER TYPE default::Movie { 25 | CREATE LINK director -> default::Person; 26 | }; 27 | ALTER TYPE default::Person { 28 | CREATE MULTI LINK acted_in := (. default::Content; 33 | CREATE REQUIRED PROPERTY name -> std::str; 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The EdgeDB MCU sandbox 🦹 2 | 3 | This is a sandbox for playing with EdgeDB and the EdgeQL query builder. 4 | 5 | It includes a simple movie database schema (`dbschema/default.esdl`) and a sample dataset (`seed.ts`) containing the movies and shows in the Marvel Cinematic Universe (last update: July 2022). 6 | 7 | ## Setup 8 | 9 | #### 1. Install the EdgeDB CLI 10 | 11 | ```bash 12 | # macOS/Linux 13 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh 14 | 15 | # windows 16 | $ iwr https://ps1.edgedb.com -useb | iex 17 | ``` 18 | 19 | #### 2. Clone the repo 20 | 21 | ```bash 22 | $ git clone git@github.com:edgedb/mcu-sandbox.git 23 | $ cd mcu-sandbox 24 | ``` 25 | 26 | #### 3. Initialize the EdgeDB project 27 | 28 | ```bash 29 | edgedb project init 30 | ``` 31 | 32 | Then follow the prompts. This step spins up a local EdgeDB instance and applies the migrations inside `dbschema/migrations`. 33 | 34 | #### 4. Setup project 35 | 36 | ```bash 37 | $ npm install # install dependencies 38 | $ npx edgeql-js # generate query builder 39 | $ npx tsx seed.ts # seed the database 40 | ``` 41 | 42 | #### 5. Start writing queries! 43 | 44 | Write a query in `script.ts` and execute it like so: 45 | 46 | ```bash 47 | $ npm run dev 48 | ``` 49 | 50 | This starts a watcher, so every time you update and save `script.ts`, the script will be re-run. 51 | 52 | ## Evolving the schema 53 | 54 | 1. Update the schema in `dbschema/default.esdl` 55 | 2. Generate a new migration with `edgedb migration create` 56 | 3. Follow the interactive prompts 57 | 4. Apply the migration with `edgedb migrate` 58 | 5. Regenerate the query builder with `npx edgeql-js` 59 | 60 | ## File structure 61 | 62 | The sandbox is a simple project designed to showcase the query builder. 63 | 64 | - `script.ts` - a simple script you can update to play with the query builder 65 | - `dbschema/default.esdl` - the schema file 66 | - `dbschema/migrations` - the migrations directory 67 | - `dbschema/edgeql-js` - the default location of the generated query builder 68 | -------------------------------------------------------------------------------- /seed.ts: -------------------------------------------------------------------------------- 1 | import * as edgedb from "edgedb"; 2 | import e from "./dbschema/edgeql-js"; 3 | 4 | const characterNames: Record = { 5 | "Robert Downey Jr.": "Iron Man", 6 | "Chris Evans": "Captain America", 7 | "Mark Ruffalo": "The Hulk", 8 | "Jeremy Renner": "Hawkeye", 9 | "Tom Holland": "Spider-Man", 10 | "Scarlett Johansson": "Black Widow", 11 | "Florence Pugh": "Yelena Belova", 12 | "Chris Hemsworth": "Thor", 13 | "Anthony Mackie": "The Falcon", 14 | "Tessa Thomson": "Valkyrie", 15 | "Paul Rudd": "Ant-Man", 16 | "Evangeline Lilly": "The Wasp", 17 | "Benedict Cumberbatch": "Doctor Strange", 18 | "Chadwick Boseman": "Black Panther", 19 | "Elizabeth Olsen": "Scarlet Witch", 20 | "Aaron Taylor-Johnson": "Quicksilver", 21 | "Paul Bettany": "Vision", 22 | "Brie Larson": "Captain Marvel", 23 | "Don Cheadle": "War Machine", 24 | "Dave Bautista": "Drax", 25 | "Zoe Saldana": "Gamora", 26 | "Chris Pratt": "Star-Lord", 27 | "Bradley Cooper": "Rocket", 28 | "Karen Gillan": "Nebula", 29 | "Vin Diesel": "Groot", 30 | "Michael Rooker": "Yondu", 31 | "Pom Klementieff": "Mantis", 32 | "Samuel L. Jackson": "Nick Fury", 33 | "Ray Winstone": "General Dreykov", 34 | "Salma Hayek": "Ajak", 35 | "Gemma Chan": "Sersi", 36 | "Richard Madden": "Ikaris", 37 | "Kumail Nanjiani": "Kingo", 38 | "Lia McHugh": "Sprite", 39 | "Brian Tyree Henry": "Phastos", 40 | "Lauren Ridloff": "Makkari", 41 | "Barry Keoghan": "Druig", 42 | "Don Lee": "Gilgamesh", 43 | "Angelina Jolie": "Thena", 44 | "Kit Harington": "Black Knight", 45 | "Danai Durira": "Okoye", 46 | "Letitia Wright": "Shuri", 47 | "Benedict Wong": "Wong", 48 | "Simu Liu": "Shang Chi", 49 | "Sam Rockwell": "Justin Hammer", 50 | "Mickey Rourke": "Whiplash", 51 | "Tom Hiddleston": "Loki", 52 | "Tim Roth": "Abomination", 53 | "Hugo Weaving": "Red Skull", 54 | "Ben Kingsley": "The Mandarin", 55 | "Gu Pearce": "Aldrich Killian", 56 | "Chris Eccleston": "Malekith", 57 | "Sebastian Stan": "The Winter Soldier", 58 | "Lee Pace": "Ronin the Accuser", 59 | "James Spader": "Ultron", 60 | "Daniel Bruhl": "Zemo", 61 | "Mads Mikkelsen": "Kaecilius", 62 | "Kurt Russell": "Ego", 63 | "Michael Keaton": "The Vulture", 64 | "Bokeem Woodbine": "Shocker", 65 | "Michael Chernus": "Tinkerer", 66 | "Cate Blanchett": "Hela", 67 | "Michael B. Jordan": "Killmonger", 68 | "Josh Brolin": "Thanos", 69 | "RaeLynn BrattenXXX": "Ghost", 70 | "Jude Law": "Yon-Rogg", 71 | "Jake Gyllenhaal": "Mysterio", 72 | "Olga Kurylenko": "Taskmaster", 73 | "Alfred Molina": "Doc Ock", 74 | "Willem Dafoe": "Green Goblin", 75 | "Jamie Foxx": "Electro", 76 | "Thomas Haden Church": "Sandman", 77 | "Rhys Ifans": "The Lizard", 78 | "Kathryn Hahn": "Agatha Harkness", 79 | "Owen Wilson": "Mobius", 80 | "Sophia Di Martino": "Sylvie", 81 | "Jonathan Majors": "Kang", 82 | "Erin Kellyman": "Karli Morgenthau", 83 | "Wyatt Russell": "U.S. Agent", 84 | "Hailee Steinfeld": "Kate Bishop", 85 | "Oscar Isaac": "Moon Knight", 86 | "Ethan Hawke": "Arthur Harrow", 87 | "Iman Vellani": "Ms. Marvel", 88 | "Christian Bale": "Gorr", 89 | "Jainie Alexander": "Sif", 90 | "Taika Waititi": "Korg", 91 | "Russell Crowe": "Zeus", 92 | "Natalie Portman": "Jane Foster", 93 | "Lupita Nyong'o": "Nakia", 94 | "Winston Duke": "M'Baku", 95 | }; 96 | 97 | const fullMovies = [ 98 | { title: "Iron Man", release_year: 2008, characters: ["Iron Man"] }, 99 | { 100 | title: "The Incredible Hulk", 101 | release_year: 2008, 102 | characters: ["The Hulk", "Abomination"], 103 | }, 104 | { 105 | title: "Iron Man 2", 106 | release_year: 2010, 107 | characters: ["Iron Man", "Black Widow", "Justin Hammer", "Whiplash"], 108 | }, 109 | { title: "Thor", release_year: 2010, characters: ["Thor", "Loki"] }, 110 | { 111 | title: "Captain America: The First Avenger", 112 | release_year: 2011, 113 | characters: ["Captain America", "Red Skull"], 114 | }, 115 | { 116 | title: "The Avengers", 117 | release_year: 2012, 118 | characters: [ 119 | "Iron Man", 120 | "Thor", 121 | "Captain America", 122 | "Hawkeye", 123 | "Black Widow", 124 | "The Hulk", 125 | "Loki", 126 | ], 127 | }, 128 | { 129 | title: "Iron Man 3", 130 | release_year: 2013, 131 | characters: ["Iron Man", "The Mandarin", "Aldrich Killian"], 132 | }, 133 | { 134 | title: "Thor: The Dark World", 135 | release_year: 2013, 136 | characters: ["Thor", "Loki", "Malekith"], 137 | }, 138 | { 139 | title: "Captain America: The Winter Soldier", 140 | release_year: 2014, 141 | characters: [ 142 | "Captain America", 143 | "The Winter Soldier", 144 | "Black Widow", 145 | "Nick Fury", 146 | ], 147 | }, 148 | { 149 | title: "Guardians of the Galaxy", 150 | release_year: 2014, 151 | characters: [ 152 | "Drax", 153 | "Gamora", 154 | "Star-Lord", 155 | "Rocket", 156 | "Nebula", 157 | "Groot", 158 | "Yondu", 159 | "Ronin the Accuser", 160 | ], 161 | }, 162 | { 163 | title: "Avengers: Age of Ultron", 164 | release_year: 2015, 165 | characters: [ 166 | "Iron Man", 167 | "Thor", 168 | "Captain America", 169 | "Hawkeye", 170 | "Black Widow", 171 | "The Hulk", 172 | "Scarlet Witch", 173 | "Quicksilver", 174 | "Ultron", 175 | "Nick Fury", 176 | "Vision", 177 | ], 178 | }, 179 | { title: "Ant-Man", release_year: 2015, characters: ["Ant-Man"] }, 180 | { 181 | title: "Captain America: Civil War", 182 | release_year: 2016, 183 | characters: [ 184 | "Captain America", 185 | "Iron Man", 186 | "Black Widow", 187 | "Hawkeye", 188 | "Spider-Man", 189 | "The Winter Soldier", 190 | "Ant-Man", 191 | "Nick Fury", 192 | "Vision", 193 | "The Falcon", 194 | "Scarlet Witch", 195 | "Black Panther", 196 | "War Machine", 197 | "Zemo", 198 | ], 199 | }, 200 | { 201 | title: "Doctor Strange", 202 | release_year: 2016, 203 | characters: ["Doctor Strange", "Wong", "Kaecilius"], 204 | }, 205 | { 206 | title: "Guardians of the Galaxy Vol. 2", 207 | release_year: 2017, 208 | characters: [ 209 | "Drax", 210 | "Gamora", 211 | "Star-Lord", 212 | "Rocket", 213 | "Nebula", 214 | "Groot", 215 | "Ego", 216 | "Mantis", 217 | "Yondu", 218 | ], 219 | }, 220 | { 221 | title: "Spider-Man: Homecoming", 222 | release_year: 2017, 223 | characters: [ 224 | "Spider-Man", 225 | "Iron Man", 226 | "The Vulture", 227 | "Shocker", 228 | "Tinkerer", 229 | ], 230 | }, 231 | { 232 | title: "Thor: Ragnarok", 233 | release_year: 2017, 234 | characters: ["Thor", "Valkyrie", "Loki", "Hela"], 235 | }, 236 | { 237 | title: "Black Panther", 238 | release_year: 2018, 239 | characters: ["Black Panther", "Shuri", "Okoye", "Killmonger"], 240 | }, 241 | { 242 | title: "Avengers: Infinity War", 243 | release_year: 2018, 244 | characters: [ 245 | "Iron Man", 246 | "Thanos", 247 | "Thor", 248 | "Captain America", 249 | "Doctor Strange", 250 | "Wong", 251 | "Scarlet Witch", 252 | "Vision", 253 | "Nebula", 254 | "Black Panther", 255 | "Okoye", 256 | "Shuri", 257 | "The Falcon", 258 | "The Hulk", 259 | "The Winter Soldier", 260 | "Black Widow", 261 | "Spider-Man", 262 | "Star-Lord", 263 | "Gamora", 264 | "Mantis", 265 | "Rocket", 266 | "Groot", 267 | "Drax", 268 | "War Machine", 269 | "Loki", 270 | ], 271 | }, 272 | { 273 | title: "Ant-Man and the Wasp", 274 | release_year: 2018, 275 | characters: ["Ant-Man", "The Wasp", "Ghost"], 276 | }, 277 | { 278 | title: "Captain Marvel", 279 | release_year: 2019, 280 | characters: ["Captain Marvel", "Nick Fury", "Yon-Rogg"], 281 | }, 282 | { 283 | title: "Avengers: Endgame", 284 | release_year: 2019, 285 | characters: [ 286 | "Ant-Man", 287 | "The Wasp", 288 | "Captain Marvel", 289 | "Hawkeye", 290 | "Iron Man", 291 | "Thanos", 292 | "Thor", 293 | "Captain America", 294 | "Doctor Strange", 295 | "Wong", 296 | "Scarlet Witch", 297 | "Vision", 298 | "Nebula", 299 | "Black Panther", 300 | "Okoye", 301 | "Shuri", 302 | "The Falcon", 303 | "The Hulk", 304 | "The Winter Soldier", 305 | "Black Widow", 306 | "Spider-Man", 307 | "Star-Lord", 308 | "Gamora", 309 | "Mantis", 310 | "Rocket", 311 | "Groot", 312 | "Drax", 313 | "War Machine", 314 | "Loki", 315 | "Valkyrie", 316 | ], 317 | }, 318 | { 319 | title: "Spider-Man: Far From Home", 320 | release_year: 2019, 321 | characters: ["Spider-Man", "Mysterio", "Nick Fury"], 322 | }, 323 | { 324 | title: "Black Widow", 325 | release_year: 2021, 326 | characters: [ 327 | "Black Widow", 328 | "Yelena Belova", 329 | "Taskmaster", 330 | "General Dreykov", 331 | ], 332 | }, 333 | { 334 | title: "Shang Chi and the Legend of the Ten Rings", 335 | release_year: 2021, 336 | characters: ["Shang Chi", "The Mandarin"], 337 | }, 338 | { 339 | title: "Eternals", 340 | release_year: 2021, 341 | characters: [ 342 | "Ajak", 343 | "Sersi", 344 | "Ikaris", 345 | "Kingo", 346 | "Sprite", 347 | "Phastos", 348 | "Makkari", 349 | "Druig", 350 | "Gilgamesh", 351 | "Thena", 352 | "Black Knight", 353 | ], 354 | }, 355 | { 356 | title: "Spider-Man: No Way Home", 357 | release_year: 2021, 358 | characters: [ 359 | "Spider-Man", 360 | "Doc Ock", 361 | "Green Goblin", 362 | "Doctor Strange", 363 | "Electro", 364 | "Sandman", 365 | "The Lizard", 366 | ], 367 | }, 368 | { 369 | title: "Doctor Strange in the Multiverse of Madness", 370 | release_year: 2022, 371 | characters: ["Doctor Strange", "Scarlet Witch", "Wong"], 372 | }, 373 | { 374 | title: "Thor: Love and Thunder", 375 | release_year: 2022, 376 | characters: [ 377 | "Thor", 378 | "Gorr", 379 | "Valkyrie", 380 | "Sif", 381 | "Korg", 382 | "Zeus", 383 | "Jane Foster", 384 | ], 385 | }, 386 | { 387 | title: "Black Panther: Wakanda Forever", 388 | release_year: 2022, 389 | characters: ["Shuri", "Nakia", "Okoye", "M'Baku"], 390 | }, 391 | ]; 392 | 393 | const tvShows = [ 394 | { 395 | title: "WandaVision", 396 | num_seasons: 1, 397 | characters: ["Scarlet Witch", "Vision", "Agatha Harkness"], 398 | }, 399 | { 400 | title: "The Falcon and the Winter Soldier", 401 | num_seasons: 1, 402 | characters: [ 403 | "The Winter Soldier", 404 | "The Falcon", 405 | "Zemo", 406 | "Karli Morgenthau", 407 | "U.S. Agent", 408 | ], 409 | }, 410 | { 411 | title: "Loki", 412 | num_seasons: 1, 413 | characters: ["Loki", "Sylvie", "Kang", "Mobius"], 414 | }, 415 | { 416 | title: "Hawkeye", 417 | num_seasons: 1, 418 | characters: ["Hawkeye", "Kate Bishop", "Yelena Belova"], 419 | }, 420 | { 421 | title: "Moon Knight", 422 | num_seasons: 1, 423 | characters: ["Moon Knight", "Arthur Harrow"], 424 | }, 425 | { 426 | title: "Ms. Marvel", 427 | num_seasons: 1, 428 | characters: ["Ms. Marvel"], 429 | }, 430 | ]; 431 | 432 | const charToActor: { [k: string]: string } = {}; 433 | const actorNames = Object.keys(characterNames); 434 | for (const actor in characterNames) { 435 | charToActor[characterNames[actor]] = actor; 436 | } 437 | 438 | const missing = new Set(); 439 | for (const m of fullMovies) { 440 | for (const c of m.characters) { 441 | if (!charToActor[c]) missing.add(c); 442 | } 443 | } 444 | 445 | async function run() { 446 | const client = edgedb.createClient(); 447 | 448 | try { 449 | await e.delete(e.Movie).run(client); 450 | await e.delete(e.Person).run(client); 451 | 452 | for (const actor of actorNames) { 453 | await e.insert(e.Person, { name: actor }).run(client); 454 | } 455 | 456 | for (const movie of fullMovies) { 457 | console.log(`Creating ${movie.title}...`); 458 | const actors = movie.characters.map((char) => ({ 459 | actor: charToActor[char], 460 | char, 461 | })); 462 | 463 | const newMovie = await e 464 | .insert(e.Movie, { 465 | title: movie.title, 466 | release_year: movie.release_year, 467 | }) 468 | .run(client); 469 | 470 | for (const actor of actors) { 471 | const query = e.update(e.Movie, (movie) => ({ 472 | filter: e.op(movie.id, "=", e.uuid(newMovie.id)), 473 | set: { 474 | actors: { 475 | "+=": e.select(e.Person, (person) => ({ 476 | "@character_name": e.str(actor.char), 477 | filter: e.op(person.name, "=", actor.actor), 478 | })), 479 | }, 480 | }, 481 | })); 482 | await query.run(client); 483 | } 484 | } 485 | 486 | for (const show of tvShows) { 487 | console.log(`Creating ${show.title}...`); 488 | const actors = show.characters.map((char) => ({ 489 | actor: charToActor[char], 490 | char, 491 | })); 492 | 493 | const newShow = await e 494 | .insert(e.TVShow, { 495 | title: show.title, 496 | num_seasons: show.num_seasons, 497 | }) 498 | .run(client); 499 | 500 | for (const actor of actors) { 501 | const query = e.update(e.TVShow, (show) => ({ 502 | filter: e.op(show.id, "=", e.uuid(newShow.id)), 503 | set: { 504 | actors: { 505 | "+=": e.select(e.Person, (person) => ({ 506 | "@character_name": e.str(actor.char), 507 | filter: e.op(person.name, "=", actor.actor), 508 | })), 509 | }, 510 | }, 511 | })); 512 | await query.run(client); 513 | } 514 | } 515 | 516 | console.log(`Creating franchise Marvel Cinematic Universe...`); 517 | const newFranchise = e.insert(e.Franchise, { 518 | name: "Marvel Cinematic Universe", 519 | entries: e.Content, // add all content 520 | }); 521 | await e.select(newFranchise, () => ({ id: true, name: true })).run(client); 522 | } catch (err) { 523 | console.log(err); 524 | } 525 | 526 | console.log(`SEEDING COMPLETE.`); 527 | client.close(); 528 | } 529 | 530 | run(); 531 | --------------------------------------------------------------------------------