├── README.md ├── logo-fauna.png └── shell.jpg /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Getting started with FQL (Fauna Query Language) 6 | 7 | This is not an exhaustive documentation but a series of ideas and examples that can help you approach the official docs more easily. I found FQL to be difficult to grasp until it *clicks*. Each section builds on the previous ones so if you are just skimming and don't understand something you probably need to go back. 8 | 9 | I'm also learning FQL as I go along so don't hesitate to correct me. 10 | 11 | To get started with Fauna just open a free account and go to the [dashboard](https://dashboard.fauna.com/). 12 | 13 | If you get stuck sign into [Fauna's community Slack](https://community-invite.fauna.com/). There are people there from all timezones willing to help. You can also ask in StackOverflow using the [`faunadb`](https://stackoverflow.com/questions/tagged/faunadb) tag. 14 | 15 | ## Sections: 16 | * Using the dashboard shell 17 | * Naming conventions 18 | * Documents and collections 19 | * Relationships and references 20 | * Indexes 21 | * Gettings multiple documents from references 22 | * Sorting results 23 | * Filtering results 24 | * About paths and using Select 25 | * Getting custom results instead of full documents 26 | 27 | ## Using the dashboard shell 28 | ![Fauna Dashboard Shell](shell.jpg) 29 | 30 | You can input FQL queries directly in the web dashboard using a shell. It's a great way of figuring out how FQL works and it remembers the queries you've made. 31 | 32 | #### Shortcuts: 33 | * Submit current query scope: CMD + Enter 34 | * Previous query: CMD + UP 35 | * Next query: CMD + DOWN 36 | * Undo: CMD + Z 37 | 38 | In Windows just change CMD for Ctrl 39 | 40 | ## Naming conventions 41 | AFAIK there are no naming conventions in Fauna but this is what I've been doing and it has stuck: 42 | * Collections go in PascalCase. Eg: `MyFavoriteThings`. 43 | * Document properties go in camelCase. Eg: `musicalInstrument`. 44 | * References in a document always end with `Ref` or `Refs` in the case of arrays. Eg: `userRef` or `productsRefs`. 45 | * Indexes go in snake_case with collections and properties as they exist in the database. Eg: `JazzArtists_by_musicalIntrument`. 46 | 47 | ## Documents and collections 48 | Fauna doesn't have tables like traditional relational databases. The smallest unit of data is a schemaless document. These are organized in collections which are essentially buckets of documents with no order whatsoever. Fauna databases can also have children databases. 49 | 50 | This is what a document looks like: 51 | ```js 52 | { 53 | "ref": Ref(Collection("Fruits"), "264471980339626516"), 54 | "ts": 1588478985090000, 55 | "data": { 56 | "name": "Mango" 57 | } 58 | } 59 | ``` 60 | 61 | Each document is composed by: 62 | * A reference (`ref` in short). More on refs later, but this represents a location in the database. In this case `Ref(Collection("Fruits"), "264471980339626516")` represents the document with the id `264471980339626516` in the `Fruits` collection. 63 | * A timestamp `ts` in microseconds. 64 | * Some `data` that looks and behaves pretty much like JavaScript objects with [some special Fauna types](https://docs.fauna.com/fauna/current/api/fql/types) like `Ref`. 65 | 66 | [More about documents here.](https://docs.fauna.com/fauna/current/api/fql/documents) 67 | 68 | Let's create a collection with [CreateCollection](https://docs.fauna.com/fauna/current/api/fql/functions/createcollection): 69 | ```js 70 | CreateCollection({ name: "Fruits" }) 71 | 72 | // Result: 73 | 74 | { 75 | "ref": Collection("Fruits"), 76 | "ts": 1588478932770000, 77 | "history_days": 30, 78 | "name": "Fruits" 79 | } 80 | ``` 81 | 82 | As you can see collections are similar to documents with some special properties. Let's keep the defaults for now but you can [read more about collections here](https://docs.fauna.com/fauna/current/api/fql/collections). 83 | 84 | Now let's create a document with [Create](https://docs.fauna.com/fauna/current/api/fql/functions/create): 85 | ```js 86 | Create( 87 | Collection("Fruits"), 88 | { 89 | data: { 90 | name: "Mango" 91 | } 92 | } 93 | ) 94 | 95 | // Result: 96 | 97 | { 98 | "ref": Ref(Collection("Fruits"), "264471980339626516"), 99 | "ts": 1588478985090000, 100 | "data": { 101 | "name": "Mango" 102 | } 103 | } 104 | ``` 105 | * `Create` is the function used to create documents. 106 | * `Collection('Fruits')` gets a reference to the collection `Fruits`. 107 | * `{data: {name: 'Mango'}}` is the document you want to create. 108 | 109 | See the [CRUD tutorial](https://docs.fauna.com/fauna/current/tutorials/crud) for more info on how to manipulate documents. 110 | 111 | #### Create a document with a predefined id 112 | First you need to retrieve a new unique id in the Fauna cluster with [NewId](https://docs.fauna.com/fauna/current/api/fql/functions/newid): 113 | ```js 114 | NewId() 115 | 116 | // Result: 117 | 118 | "264517098691101203" 119 | ``` 120 | And then you can create your new document with a reference to the provided `id` instead: 121 | ```js 122 | Create( 123 | Ref(Collection("Fruits"), "264517098691101203"), 124 | { 125 | data: { 126 | name: "Apple" 127 | } 128 | } 129 | ) 130 | ``` 131 | 132 | ## Relationships and references 133 | Relationships in Fauna are modelled with references. A [Ref](https://docs.fauna.com/fauna/current/api/fql/functions/ref) is a type of data in Fauna which points to a location in the database. For documents this reference includes an `id` property which would be the `264517098691101203` in the previous example. 134 | 135 | Remember that a reference is not a thing, it's a pointer to a thing (documents, collections, etc). 136 | 137 | If you want to retrieve a document from a reference you need to use the [Get](https://docs.fauna.com/fauna/current/api/fql/functions/get) function: 138 | ```js 139 | Get( 140 | Ref( 141 | Collection("Fruits"), 142 | "264471980339626516" 143 | ) 144 | ) 145 | 146 | // Result: 147 | 148 | { 149 | "ref": Ref(Collection("Fruits"), "264471980339626516"), 150 | "ts": 1588478985090000, 151 | "data": { 152 | "name": "Mango" 153 | } 154 | } 155 | ``` 156 | So this is how you could model a relationship between documents: 157 | ```js 158 | Create( 159 | Collection("People"), 160 | { 161 | data: { 162 | name: "Pier", 163 | favoriteFruitRef: Ref(Collection("Fruits"), "264471980339626516") 164 | } 165 | } 166 | ) 167 | ``` 168 | Or: 169 | ```js 170 | Create( 171 | Collection("People"), 172 | { 173 | data: { 174 | name: "Pier", 175 | favoriteFruitsRefs: [ 176 | Ref(Collection("Fruits"), "264471980339626516"), 177 | Ref(Collection("Fruits"), "467478980389696500") 178 | ] 179 | } 180 | } 181 | ) 182 | ``` 183 | **Note:** If you're coming from SQL you might be tempted to store raw ids in your documents but it's much better to use the native `Ref` type as it will make your FQL life simpler. 184 | 185 | See the [E-commerce tutorial](https://docs.fauna.com/fauna/current/tutorials/ecommerce) for more info on modeling relational data with Fauna. 186 | 187 | ## Indexes 188 | Retrieving one document at a time with `Get` won't get you very far. Since collections are just buckets of documents, you need to use indexes to create some order, retreive multiple documents, sort, filter, and more. 189 | 190 | The combination of a collection and an index is probably the closest you will get to a table of a relational database. The big difference being that table rows can only belong to a single table. In contrast, Fauna indexes can have multiple collections as sources of data and documents can belong to as many indexes as you need. 191 | 192 | Let's create our first index with [CreateIndex](https://docs.fauna.com/fauna/current/api/fql/functions/createindex): 193 | ```js 194 | CreateIndex({ 195 | name: "all_Fruits", 196 | source: Collection("Fruits") 197 | }) 198 | 199 | // Result: 200 | 201 | { 202 | "ref": Index("all_Fruits"), 203 | "ts": 1588482162446000, 204 | "active": true, 205 | "serialized": true, 206 | "name": "all_Fruits", 207 | "source": Collection("Fruits"), 208 | "partitions": 8 209 | } 210 | ``` 211 | 212 | As you can see indexes are also a special type of document. Pretty much everything in Fauna is a document. 213 | 214 | After adding some more fruits to your `Fruits` collection let's retreive all the references using the `all_Fruits` index we just created: 215 | ```js 216 | Paginate( 217 | Match( 218 | Index("all_Fruits") 219 | ) 220 | ) 221 | 222 | // Result: 223 | 224 | { 225 | "data": [ 226 | Ref(Collection("Fruits"), "264471980339626516"), 227 | Ref(Collection("Fruits"), "264476914201133588"), 228 | Ref(Collection("Fruits"), "264476925331767828"), 229 | Ref(Collection("Fruits"), "264476941393854996"), 230 | Ref(Collection("Fruits"), "264476977509958164"), 231 | Ref(Collection("Fruits"), "264476994575532564"), 232 | Ref(Collection("Fruits"), "264477007396471316") 233 | ] 234 | } 235 | ``` 236 | Woah woah woah there's a lot going on there. 237 | 238 | Let's break it down. I've found sometimes it makes more sense to go backwards with FQL: 239 | * [Index](https://docs.fauna.com/fauna/current/api/fql/functions/index) returns a reference to an index. 240 | * [Match](https://docs.fauna.com/fauna/current/api/fql/functions/match) combines the index with some optional parameters to feed `Paginate` with the necessary configuration to actually retrieve the matching data. At this point no actual data has been yet retrieved from Fauna. 241 | * [Paginate](https://docs.fauna.com/fauna/current/api/fql/functions/paginate) takes the output of `Match` (actually a reference to a [Set](https://docs.fauna.com/fauna/current/api/fql/types#set)), retrieves data from Fauna, and finally returns a [Page](https://docs.fauna.com/fauna/current/api/fql/types#page) with an array of references. 242 | 243 | Later on we'll see how to get actual documents instead of references and how to use `Match` for filtering. 244 | 245 | #### Page size 246 | By default `Paginate` returns pages with 64 items. You can define how many items you want to receive with `size`: 247 | ```js 248 | Paginate( 249 | Match(Index('all_Fruits')), 250 | {size: 3} 251 | ) 252 | 253 | // Result: 254 | 255 | { 256 | "after": [ 257 | Ref(Collection("Fruits"), "264476941393854996") 258 | ], 259 | "data": [ 260 | Ref(Collection("Fruits"), "264471980339626516"), 261 | Ref(Collection("Fruits"), "264476914201133588"), 262 | Ref(Collection("Fruits"), "264476925331767828") 263 | ] 264 | } 265 | ``` 266 | Fauna gives us the next document with `after` to use as a cursor because in this case there are more results than fit in a single page of `3`. 267 | 268 | See the [Paginate](https://docs.fauna.com/fauna/current/api/fql/functions/paginate) docs for more info on using cursors. 269 | 270 | #### Unique values 271 | You can also use indexes to determine that a property of a document must be unique in the whole collection much like the `UNIQUE` constraint in SQL. This must be defined when creating the index: 272 | ```js 273 | CreateIndex({ 274 | name: "Users_by_email", 275 | source: Collection("Users"), 276 | terms: [ 277 | {field: ["data", "email"]} 278 | ], 279 | unique: true 280 | }) 281 | ``` 282 | Even if you never use that index to retrieve documents it will ensure the `email` property in the `data` part of your document is unique accross the `Users` collection. Fauna will return an error if you try to insert a document that breaks that constraint. 283 | 284 | We'll see what that `terms` object is later on. 285 | 286 | **Note:** `["data", "email"]` represents a path. We'll see more about paths later but you can think of those as `data/email` or even `data.email`. 287 | 288 | ## Gettings multiple documents from references 289 | Ok so we know how to get a list of references from an index. How do we actually get documents? 290 | 291 | When programming you might have come across `map` which executes a function on each item of an array and returns a new array. For example in JavaScript we use [`myArray.map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). 292 | 293 | Fauna also has a [Map](https://docs.fauna.com/fauna/current/api/fql/functions/index) function. We can use it to retrieve an array of documents from an array of references: 294 | ```js 295 | Map( 296 | Paginate(Match(Index("all_Fruits"))), 297 | Lambda("fruitRef", Get(Var("fruitRef"))) 298 | ) 299 | 300 | // Result: 301 | 302 | { 303 | "data": [ 304 | { 305 | "ref": Ref(Collection("Fruits"), "264471980339626516"), 306 | "ts": 1588478985090000, 307 | "data": { 308 | "name": "Mango" 309 | } 310 | }, 311 | { 312 | "ref": Ref(Collection("Fruits"), "264476914201133588"), 313 | "ts": 1588483690410000, 314 | "data": { 315 | "name": "Apple" 316 | } 317 | }, 318 | // etc 319 | ] 320 | } 321 | ``` 322 | Don't panic, let's break this down. 323 | 324 | We already understand from a previous example that `Paginate(Match(Index('all_Fruits')))` returns an array of references, right? So `Map` iterates over this array, executes `Lambda` on each item, and returns a new array with documents. 325 | 326 | This is the tricky part: `Lambda("fruitRef", Get(Var("fruitRef")))`: 327 | * [Lambda](https://docs.fauna.com/fauna/current/api/fql/functions/lambda) is used in FQL to define anonymous functions. In this example we are defining a function that will take a reference and return a document. 328 | * `Lambda("fruitRef"...` defines a function parameter. It could be named anything: `fruit`, `X`, `Tarzan`, etc. The name is irrelevant. In this case the parameter will receive a single document reference that `Map` will pass to `Lambda` from `Paginate`. 329 | * `Var("fruitRef")` evaluates the variable named `fruitRef` in the context of the function. You couldn't simply use `fruitRef` or `"fruitRef"` because FQL wouldn't know what do with it. [Var](https://docs.fauna.com/fauna/current/api/fql/functions/lambda) explicitly tells Fauna to find and evaluate a variable in the current context. 330 | * Finally `Get` receives a reference from `Var` and returns an actual document. This document is returned by `Lambda` to `Map` to form an array of documents. 331 | 332 | Consider this JavaScript example: 333 | ```js 334 | const newArray = myArray.map((item) => doSomething(item)); 335 | // which is equivalent to: 336 | const newArray = myArray.map(function (item) { 337 | return doSomething(item); 338 | }); 339 | ``` 340 | If you were using the JavaScript Fauna driver you could write something like this instead of using `Lambda`: 341 | ```js 342 | const result = await client.query( 343 | q.Map( 344 | q.Paginate(q.Match(q.Index('all_Fruits'))), 345 | (fruitRef) => q.Get(fruitRef) 346 | ) 347 | ) 348 | ``` 349 | ## Sorting results 350 | To retrieve a list of ordered documents first we need to create a new index. 351 | ```js 352 | CreateIndex({ 353 | name: "all_Fruits_sorted_by_name", 354 | source: Collection("Fruits"), 355 | values: [ 356 | { field: ["data", "name"] }, 357 | { field: ["ref"] } 358 | ] 359 | }) 360 | ``` 361 | The new bit here is this `values` array. 362 | 363 | We saw earlier that indexes also accept a `terms` array. The idea is simply that `terms` are **input** parameters for our index, and `values` are the actual values the index will **output**. 364 | 365 | In this case the index will return two values for each result: 366 | * `{ field: ["data", "name"] }` will return the `name` in the `data` part of the document. 367 | * `{ field: ["ref"] }` will return a reference to the document. 368 | 369 | Proof: 370 | ```js 371 | Paginate(Match(Index("all_Fruits_sorted_by_name"))) 372 | 373 | // Result: 374 | 375 | { 376 | "data": [ 377 | [ 378 | "Apple", 379 | Ref(Collection("Fruits"), "264476914201133588") 380 | ], 381 | [ 382 | "Avocado", 383 | Ref(Collection("Fruits"), "264476977509958164") 384 | ], 385 | [ 386 | "Banana", 387 | Ref(Collection("Fruits"), "264476925331767828") 388 | ], 389 | [ 390 | "Lemon", 391 | Ref(Collection("Fruits"), "264477007396471316") 392 | ], 393 | // etc 394 | ] 395 | } 396 | ``` 397 | As you can see the documents are already sorted by `name` alphabetically. Fauna will automatically sort the results by the fields you add to `values`. 398 | 399 | If you want to reverse the order of the results you'd need to create a new index and use the `reverse` setting: 400 | ```js 401 | CreateIndex({ 402 | name: "all_Fruits_sorted_by_name_reverse", 403 | source: Collection("Fruits"), 404 | values: [ 405 | { field: ["data", "name"], reverse: true }, 406 | { field: ["ref"] } 407 | ] 408 | }) 409 | ``` 410 | Later on we'll see how to actually get a list of documents from these results. 411 | 412 | ## Filtering results 413 | Filtering is similar to sorting and is done using indexes. Instead of adding `values` we need to add a `terms` array. 414 | 415 | Remember: `terms` is input, `values` is output. 416 | 417 | ```js 418 | CreateIndex({ 419 | name: "Fruits_by_name", 420 | source: Collection("Fruits"), 421 | terms: [ 422 | { field: ["data", "name"]} 423 | ] 424 | }) 425 | ``` 426 | So now we can use `Match` to pass a parameter to our index: 427 | ```js 428 | Paginate( 429 | Match(Index("Fruits_by_name"), "Apple") 430 | ) 431 | 432 | // Result: 433 | 434 | { 435 | "data": [ 436 | Ref(Collection("Fruits"), "264476914201133588") 437 | ] 438 | } 439 | ``` 440 | Or if we want to get documents instead of references: 441 | ```js 442 | Map( 443 | Paginate(Match(Index("Fruits_by_name"), "Apple")), 444 | Lambda("fruitRef", Get(Var("fruitRef"))) 445 | ) 446 | 447 | // Result: 448 | 449 | { 450 | "data": [ 451 | { 452 | "ref": Ref(Collection("Fruits"), "264476914201133588"), 453 | "ts": 1588483690410000, 454 | "data": { 455 | "name": "Apple" 456 | } 457 | } 458 | ] 459 | } 460 | ``` 461 | If we know in advance we will get a single result we could simply use `Get` instead of `Paginate`: 462 | ```js 463 | Get(Match(Index("Fruits_by_name"), "Apple")) 464 | 465 | // Result: 466 | 467 | { 468 | "ref": Ref(Collection("Fruits"), "264476914201133588"), 469 | "ts": 1588483690410000, 470 | "data": { 471 | "name": "Apple" 472 | } 473 | } 474 | ``` 475 | 476 | If you want to know how to sort and filter at the same time check the [Search and sort with indexes tutorial](https://docs.fauna.com/fauna/current/tutorials/indexes/search_and_sort). 477 | 478 | ## About paths and using Select 479 | Remember how we got an array of arrays when sorting our data? 480 | 481 | To recap here's the ouput we have from the index: 482 | ```js 483 | Paginate(Match(Index("all_Fruits_sorted_by_name"))) 484 | 485 | // Result: 486 | 487 | { 488 | "data": [ 489 | [ 490 | "Apple", 491 | Ref(Collection("Fruits"), "264476914201133588") 492 | ], 493 | // etc 494 | ] 495 | } 496 | ``` 497 | This is no bueno since we actually want documents and not references. 498 | 499 | The problem we have now is that we cannot use `Lambda("fruitRef", Get(Var("fruitRef")))` like we did before to get a document. If you do that you will get an error because you wouldn't be passing a `Ref` to `Get`, you'd be actually passing this array: 500 | ```js 501 | [ 502 | "Apple", 503 | Ref(Collection("Fruits"), "264476914201133588") 504 | ] 505 | ``` 506 | Damn! 507 | 508 | The solution is to find a way to select the second item of that array, which is a reference, and pass it to `Get` to retrieve a document. 509 | 510 | We're going to use [Select](https://docs.fauna.com/fauna/current/api/fql/functions/select) for this: 511 | 512 | ```js 513 | Map( 514 | Paginate(Match(Index("all_Fruits_sorted_by_name"))), 515 | Lambda("itemFromIndex", Get(Select([1], Var("itemFromIndex")))) 516 | ) 517 | 518 | // Result: 519 | 520 | { 521 | "data": [ 522 | { 523 | "ref": Ref(Collection("Fruits"), "264476914201133588"), 524 | "ts": 1588483690410000, 525 | "data": { 526 | "name": "Apple" 527 | } 528 | }, 529 | { 530 | "ref": Ref(Collection("Fruits"), "264476977509958164"), 531 | "ts": 1588483750760000, 532 | "data": { 533 | "name": "Avocado" 534 | } 535 | }, 536 | // etc 537 | ] 538 | } 539 | ``` 540 | This is the interesting bit `Select([1], Var("itemFromIndex"))`. 541 | 542 | We already know from [a previous section](#gettings-multiple-documents-from-references) that `Var("itemFromIndex")` will return whatever `Lambda` is getting into its parameter which in this case is this: 543 | ```js 544 | [ 545 | "Apple", 546 | // 👇 we want this Ref to be able to get a document! 547 | Ref(Collection("Fruits"), "264476914201133588") 548 | ] 549 | ``` 550 | To do that we pass this path `[1]` to `Select`. This will return a `Ref` that we can use with `Get` and *voila!* we will have our document. 551 | 552 | **Note:** Arrays in Fauna are zero based so we need to use the index `1` to select the second item from the array. 553 | 554 | #### Selecting document properties 555 | `Select` can also be used with paths that point to document properties (eg: `["data","name"]`) as we saw when sorting and filtering results. 556 | 557 | To clarify this point further imagine we had a document with this structure: 558 | ```js 559 | { 560 | data: { 561 | name: "Pier" 562 | country: { 563 | name: "Spain", 564 | city: { 565 | name: "Palma de Mallorca" 566 | } 567 | } 568 | } 569 | } 570 | ``` 571 | To get the city name we'd use the following path: `["data", "country", "city", "name"]`. 572 | 573 | ## Getting custom results instead of full documents 574 | Until now we've retrieved full documents but it may not make sense to do that every time we query Fauna. 575 | 576 | Imagine we have a `Posts` collection with hundreds of posts such as this one: 577 | ```js 578 | { 579 | data: { 580 | title: "Lorem ipsum", 581 | content: "This is the story about that time I took a latin class and...", 582 | tags: ["boring"], 583 | authorRef: Ref(Collection("Authors"), "264471980339626516"), 584 | status: 'DRAFT' 585 | // etc 586 | } 587 | } 588 | ``` 589 | It would be a waste of bytes to send the complete post if we only wanted to present a list of posts to the user. You could certainly solve that in your backend logic but it can be solved in FQL too. 590 | 591 | To do that we need to use [Let](https://docs.fauna.com/fauna/current/api/fql/functions/let) to create a custom expression. 592 | 593 | By this point you should already know how to create collections, documents, and indexes. Create a `Posts` collection, add some posts to it, and then create an `all_Posts` index. 594 | 595 | Done? Great. 596 | 597 | This is how we use `Let` to retreive a custom expression from Fauna instead of full documents: 598 | ```js 599 | Map( 600 | Paginate(Match(Index("all_Posts"))), 601 | Lambda("postRef", Let( 602 | { 603 | postDocument: Get(Var("postRef")) 604 | }, 605 | { 606 | id: Select(["ref", "id"], Var("postDocument")), 607 | title: Select(["data", "title"], Var("postDocument")) 608 | } 609 | )) 610 | ) 611 | 612 | // Result: 613 | 614 | { 615 | "data": [ 616 | { 617 | "id": "264538580516340242", 618 | "title": "Lorem ipsum" 619 | }, 620 | { 621 | "id": "264538588626027026", 622 | "title": "Some other post" 623 | }, 624 | { 625 | "id": "264538600151974418", 626 | "title": "And yet another boring post" 627 | }, 628 | { 629 | "id": "264538616193090066", 630 | "title": "Lorem Ipsum the second" 631 | } 632 | ] 633 | } 634 | ``` 635 | Boom shaka laka! 636 | 637 | Let's break this down. 638 | 639 | We already know what `Map`, `Paginate`, and `Lambda` are doing from previous sections. 640 | 641 | This is the interesting bit: 642 | ```js 643 | Lambda("postRef", Let( 644 | { 645 | postDocument: Get(Var("postRef")) 646 | }, 647 | { 648 | id: Select(["ref", "id"], Var("postDocument")), 649 | title: Select(["data", "title"], Var("postDocument")) 650 | } 651 | )) 652 | ``` 653 | We are defining a `Lambda` function which will return whatever `Let` returns. 654 | 655 | As you can see `Let` accepts two lists of pair values. These lists are called dictionnaries or objects in some programming languages. 656 | 657 | The first list are variables you define to be used later on. In the [docs](https://docs.fauna.com/fauna/current/api/fql/functions/let) they call this first list `bindings`: 658 | ``` 659 | { 660 | postDocument: Get(Var("postRef")) 661 | } 662 | ``` 663 | These bindings will be available in the context of our `Let`, and even other nested `Let`. Again, the name doesn't matter. It could be `post`, `X`, `whatever`, etc. 664 | 665 | The value of `postDocument` is a document. Why? Because it's the result of `Get` which we know returns a document. `Var("postRef")` is evaluating the `postRef` variable which is the input parameter of our `Lambda`. 666 | 667 | The second list of values is the **output** of `Let`, or the custom expression that `Let` will return: 668 | ```js 669 | { 670 | id: Select(["ref", "id"], Var("postDocument")), 671 | title: Select(["data", "title"], Var("postDocument")) 672 | } 673 | ``` 674 | Here we are defining `id` and `title`. Again, you can use any name you wish. In both cases we are selecting a property from the `postDocument` document by using `Select` as we saw in the previous section. 675 | 676 | If you want to see a more complex `Let` example see [this Gist](https://gist.github.com/ptpaterson/82c01afc9b0ff624f96141a078b5ab54) by [ptpaterson](https://gist.github.com/ptpaterson). 677 | -------------------------------------------------------------------------------- /logo-fauna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PierBover/getting-started-fauna-db-fql/d1649cfa87af0d5de8e03791cac139405bbcb46d/logo-fauna.png -------------------------------------------------------------------------------- /shell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PierBover/getting-started-fauna-db-fql/d1649cfa87af0d5de8e03791cac139405bbcb46d/shell.jpg --------------------------------------------------------------------------------