├── 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 | 
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
--------------------------------------------------------------------------------