Sorting GraphQL results

Problem

You want to sort results by a particular field in a GraphQL query.

Solution

You can sort GraphQL query results using an index and a user-defined function.

The following example uses the following components:

The GraphQL schema schema.gql looks like this:

type Posts {
  title: String!
  content: String!
}

type Query {
  allPostsSortedByTitle: [Posts!]! @resolver(name: "sort_by_title", paginated: true)
}

When you import the above schema, the GraphQL API automatically creates a collection named Posts and a UDF named sort_by_title.

For more information about importing GraphQL schemas, see the GraphQL quick start.

We need an index to sort our documents:

try
{
    Value result = await client.Query(
        CreateIndex(
            Obj(
                "name", "all_posts_sorted_by_title",
                "source", Collection("Posts"),
                "values", Arr(
                    Obj("field", Arr("data", "title")),
                    Obj("field", Arr("ref"))
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "all_posts_sorted_by_title", collection = RefV(id = "indexes")),ts: LongV(1626710175530000),active: BooleanV(True),serialized: BooleanV(True),name: StringV(all_posts_sorted_by_title),source: RefV(id = "Posts", collection = RefV(id = "collections")),values: Arr(ObjectV(field: Arr(StringV(data), StringV(title))), ObjectV(field: Arr(StringV(ref)))),partitions: LongV(8))
result, err := client.Query(
	f.CreateIndex(
		f.Obj{
			"name": "all_posts_sorted_by_title",
			"source": f.Collection("Posts"),
			"values": f.Arr{
				f.Obj{"field": f.Arr{"data", "title"}},
				f.Obj{"field": f.Arr{"ref"}},
			},
		}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[active:true name:all_posts_sorted_by_title partitions:8 ref:{all_posts_sorted_by_title 0xc000200ae0 0xc000200ae0 <nil>} serialized:true source:{Posts 0xc000200bd0 0xc000200bd0 <nil>} ts:1626716941490000 values:[map[field:[data title]] map[field:[ref]]]]
System.out.println(
    client.query(
        CreateIndex(
            Obj(
                "name", Value("all_posts_sorted_by_title"),
                "source", Collection("Posts"),
                "values", Arr(
                    Obj("field", Arr(Value("data"), Value("title"))),
                    Obj("field", Arr(Value("ref")))
                )
            )
        )
    ).get());
{ref: ref(id = "all_posts_sorted_by_title", collection = ref(id = "indexes")), ts: 1626717219530000, active: true, serialized: true, name: "all_posts_sorted_by_title", source: ref(id = "Posts", collection = ref(id = "collections")), values: [{field: ["data", "title"]}, {field: ["ref"]}], partitions: 8}
client.query(
  q.CreateIndex({
    name: 'all_posts_sorted_by_title',
    source: q.Collection('Posts'),
    values: [
      { field: ['data', 'title'] },
      { field: ['ref'] },
    ],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Index("all_posts_sorted_by_title"),
  ts: 1626709622330000,
  active: true,
  serialized: true,
  name: 'all_posts_sorted_by_title',
  source: Collection("Posts"),
  values: [ { field: [ 'data', 'title' ] }, { field: [ 'ref' ] } ],
  partitions: 8
}
result = client.query(
  q.create_index({
    "name": "all_posts_sorted_by_title",
    "source": q.collection("Posts"),
    "values": [
      { "field": ["data", "title"] },
      { "field": ["ref"] }
    ]
  })
)
print(result)
{'ref': Ref(id=all_posts_sorted_by_title, collection=Ref(id=indexes)), 'ts': 1626709688800000, 'active': True, 'serialized': True, 'name': 'all_posts_sorted_by_title', 'source': Ref(id=Posts, collection=Ref(id=collections)), 'values': [{'field': ['data', 'title']}, {'field': ['ref']}], 'partitions': 8}
CreateIndex({
  "name": "all_posts_sorted_by_title",
  "source": Collection("Posts"),
  "values": [
    { "field": ["data", "title"] },
    { "field": [ "ref" ] }
  ],
})
{
  ref: Index("all_posts_sorted_by_title"),
  ts: 1635289589810000,
  active: true,
  serialized: true,
  name: 'all_posts_sorted_by_title',
  source: Collection("Posts"),
  values: [ { field: [ 'data', 'title' ] }, { field: [ 'ref' ] } ],
  partitions: 8
}
Query metrics:
  •    bytesIn:   174

  •   bytesOut:   339

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     1

  •  readBytes: 1,257

  • writeBytes:   473

  •  queryTime:  54ms

  •    retries:     0

We need some documents to sort. We can do this in either FQL or GraphQL. In FQL, the document creation command looks like this:

try
{
    Value result = await client.Query(
        Map(
            Arr(
                Obj(
                    "title", "my second post",
                    "content", "more placeholder content"
                ),
                Obj(
                    "title", "my first post",
                    "content", "placeholder content"
                ),
                Obj(
                    "title", "my third post",
                    "content", "even more placeholder content"
                )
            ),
            Lambda(
                "doc",
                Create(
                    Collection("Posts"),
                    Obj("data", Var("doc"))
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
Arr(ObjectV(ref: RefV(id = "305858788460593664", collection = RefV(id = "Posts", collection = RefV(id = "collections"))),ts: LongV(1627948521020000),data: ObjectV(title: StringV(my second post),content: StringV(more placeholder content))), ObjectV(ref: RefV(id = "305858788460594688", collection = RefV(id = "Posts", collection = RefV(id = "collections"))),ts: LongV(1627948521020000),data: ObjectV(title: StringV(my first post),content: StringV(placeholder content))), ObjectV(ref: RefV(id = "305858788460595712", collection = RefV(id = "Posts", collection = RefV(id = "collections"))),ts: LongV(1627948521020000),data: ObjectV(title: StringV(my third post),content: StringV(even more placeholder content))))
result, err := client.Query(
	f.Map(
		f.Arr{
			f.Obj{
				"title": "my second post",
				"content": "more placeholder content",
			},
			f.Obj{
				"title": "my first post",
				"content": "placeholder content",
			},
			f.Obj{
				"title": "my third post",
				"content": "even more placeholder content",
			},
		},
		f.Lambda(
			"doc",
			f.Create(
				f.Collection("Posts"),
				f.Obj{ "data": f.Var("doc") },
			),
		),
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
[map[data:map[content:more placeholder content title:my second post] ref:{305858643596673536 0xc00011e180 0xc00011e180 <nil>} ts:1627948382860000] map[data:map[content:placeholder content title:my first post] ref:{305858643596674560 0xc00011e390 0xc00011e390 <nil>} ts:1627948382860000] map[data:map[content:even more placeholder content title:my third post] ref:{305858643596675584 0xc00011e5a0 0xc00011e5a0 <nil>} ts:1627948382860000]]
System.out.println(
    client.query(
        Map(
            Arr(
                Obj(
                    "title", Value("my second post"),
                    "content", Value("more placeholder content")
                ),
                Obj(
                    "title", Value("my first post"),
                    "content", Value("placeholder content")
                ),
                Obj(
                    "title", Value("my third post"),
                    "content", Value("even more placeholder content")
                )
            ),
            Lambda(
                "doc",
                Create(
                    Collection("Posts"),
                    Obj("data", Var("doc"))
                )
            )
        )
    ).get());
[{ref: ref(id = "305858428495987200", collection = ref(id = "Posts", collection = ref(id = "collections"))), ts: 1627948177730000, data: {title: "my second post", content: "more placeholder content"}}, {ref: ref(id = "305858428495988224", collection = ref(id = "Posts", collection = ref(id = "collections"))), ts: 1627948177730000, data: {title: "my first post", content: "placeholder content"}}, {ref: ref(id = "305858428495989248", collection = ref(id = "Posts", collection = ref(id = "collections"))), ts: 1627948177730000, data: {title: "my third post", content: "even more placeholder content"}}]
client.query(
  q.Map(
    [
      { title: 'my second post', content: 'more placeholder content' },
      { title: 'my first post', content: 'placeholder content' },
      { title: 'my third post', content: 'even more placeholder content' },
    ],
    q.Lambda(
      'doc',
      q.Create(
        q.Collection('Posts'),
        { data: q.Var('doc') },
      )
    )
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Ref(Collection("Posts"), "305856701000581632"),
    ts: 1627946530260000,
    data: { title: 'my second post', content: 'more placeholder content' }
  },
  {
    ref: Ref(Collection("Posts"), "305856701000582656"),
    ts: 1627946530260000,
    data: { title: 'my first post', content: 'placeholder content' }
  },
  {
    ref: Ref(Collection("Posts"), "305856701000583680"),
    ts: 1627946530260000,
    data: {
      title: 'my third post',
      content: 'even more placeholder content'
    }
  }
]
result = client.query(
  q.map_(
    q.lambda_(
      "doc",
      q.create(
        q.collection("Posts"),
        { "data": q.var("doc") }
      )
    ),
    [
      { "title": "my second post", "content": "more placeholder content" },
      { "title": "my first post", "content": "placeholder content" },
      { "title": "my third post", "content": "even more placeholder content" }
    ]
  )
)
print(result)
[{'ref': Ref(id=305856899814785536, collection=Ref(id=Posts, collection=Ref(id=collections))), 'ts': 1627946719890000, 'data': {'title': 'my second post', 'content': 'more placeholder content'}}, {'ref': Ref(id=305856899814786560, collection=Ref(id=Posts, collection=Ref(id=collections))), 'ts': 1627946719890000, 'data': {'title': 'my first post', 'content': 'placeholder content'}}, {'ref': Ref(id=305856899814787584, collection=Ref(id=Posts, collection=Ref(id=collections))), 'ts': 1627946719890000, 'data': {'title': 'my third post', 'content': 'even more placeholder content'}}]
Map(
  [
    { title: 'my second post', content: 'more placeholder content' },
    { title: 'my first post', content: 'placeholder content' },
    { title: 'my third post', content: 'even more placeholder content' },
  ],
  Lambda(
    'doc',
    Create(
      Collection('Posts'),
      { data: Var('doc') },
    )
  )
)
[
  {
    ref: Ref(Collection("Posts"), "305856982752952832"),
    ts: 1627946798960000,
    data: { title: 'my second post', content: 'more placeholder content' }
  },
  {
    ref: Ref(Collection("Posts"), "305856982752953856"),
    ts: 1627946798960000,
    data: { title: 'my first post', content: 'placeholder content' }
  },
  {
    ref: Ref(Collection("Posts"), "305856982752954880"),
    ts: 1627946798960000,
    data: {
      title: 'my third post',
      content: 'even more placeholder content'
    }
  }
]
Query metrics:
  •    bytesIn:   346

  •   bytesOut:   666

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     3

  •  readBytes:     0

  • writeBytes: 1,253

  •  queryTime:  43ms

  •    retries:     0

Or to create the documents with GraphQL, use the following mutation:

mutation {
  post2: createPosts( data:{
  	title: "my second post"
    content: "more placeholder content"
  }) {
    _id
  },
  post1: createPosts( data:{
  	title: "my first post"
    content: "placeholder content"
  }) {
    _id
  },
  post3: createPosts( data:{
  	title: "my third post"
    content: "even more placeholder content"
  }) {
    _id
  }
}

Next, update the stub function sort_by_title:

try
{
    Value result = await client.Query(
        Update(
            Function("sort_by_title"),
            Obj(
                "body", Query(
                    Lambda(
                        Arr("size", "after", "before"),
                        Let(
                            "match", Match(Index("all_posts_sorted_by_title")),
                            "page", If(
                                Equals(Var("before"), null),
                                If(
                                    Equals(Var("after"), null),
                                    Paginate(
                                        Var("match"),
                                        size: Var("size")
                                    ),
                                    Paginate(
                                        Var("match"),
                                        size: Var("size"),
                                        after: Var("after")
                                    )
                                ),
                                Paginate(
                                    Var("match"),
                                    size: Var("size"),
                                    before: Var("before")
                                )
                            )
                        ).In(
                            Map(
                                Var("page"),
                                Lambda(
                                    "values",
                                    Get(Select(1, Var("values")))
                                )
                            )
                        )
                    )
                )
            )
        )
    );

    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "sort_by_title", collection = RefV(id = "functions")),ts: LongV(1627945656760000),name: StringV(sort_by_title),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]),data: ObjectV(gql: StringV(not included for brevity)))
result, err := client.Query(
	f.Update(
		f.Function("sort_by_title"),
		f.Obj{
			"body": f.Query(
				f.Lambda(
					f.Arr{"size", "after", "before"},
					f.Let().Bind(
						"match", f.Match(f.Index("all_posts_sorted_by_title")),
					).Bind(
						"page", f.If(
							f.Equals(f.Var("before"), nil),
							f.If(
								f.Equals(f.Var("after"), nil),
								f.Paginate(
									f.Var("match"),
									f.Size(f.Var("size")),
								),
								f.Paginate(
									f.Var("match"),
									f.Size(f.Var("size")),
									f.After(f.Var("after")),
								),
							),
							f.Paginate(
								f.Var("match"),
								f.Size(f.Var("size")),
								f.Before(f.Var("before")),
							),
						),
					).In(
						f.Map(
							f.Var("page"),
							f.Lambda(
								"values",
								f.Get(f.Select(1, f.Var("values"))),
							),
						),
					),
				),
			), 
		},
	),
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 34 115 105 122 101 34 44 34 97 102 116 101 114 34 44 34 98 101 102 111 114 101 34 93 44 34 101 120 112 114 34 58 123 34 108 101 116 34 58 123 34 109 97 116 99 104 34 58 123 34 109 97 116 99 104 34 58 123 34 105 110 100 101 120 34 58 34 97 108 108 95 112 111 115 116 115 95 115 111 114 116 101 100 95 98 121 95 116 105 116 108 101 34 125 125 44 34 112 97 103 101 34 58 123 34 105 102 34 58 123 34 101 113 117 97 108 115 34 58 91 123 34 118 97 114 34 58 34 98 101 102 111 114 101 34 125 44 110 117 108 108 93 125 44 34 116 104 101 110 34 58 123 34 105 102 34 58 123 34 101 113 117 97 108 115 34 58 91 123 34 118 97 114 34 58 34 97 102 116 101 114 34 125 44 110 117 108 108 93 125 44 34 116 104 101 110 34 58 123 34 112 97 103 105 110 97 116 101 34 58 123 34 118 97 114 34 58 34 109 97 116 99 104 34 125 44 34 115 105 122 101 34 58 123 34 118 97 114 34 58 34 115 105 122 101 34 125 125 44 34 101 108 115 101 34 58 123 34 112 97 103 105 110 97 116 101 34 58 123 34 118 97 114 34 58 34 109 97 116 99 104 34 125 44 34 97 102 116 101 114 34 58 123 34 118 97 114 34 58 34 97 102 116 101 114 34 125 44 34 115 105 122 101 34 58 123 34 118 97 114 34 58 34 115 105 122 101 34 125 125 125 44 34 101 108 115 101 34 58 123 34 112 97 103 105 110 97 116 101 34 58 123 34 118 97 114 34 58 34 109 97 116 99 104 34 125 44 34 98 101 102 111 114 101 34 58 123 34 118 97 114 34 58 34 98 101 102 111 114 101 34 125 44 34 115 105 122 101 34 58 123 34 118 97 114 34 58 34 115 105 122 101 34 125 125 125 125 44 34 105 110 34 58 123 34 109 97 112 34 58 123 34 108 97 109 98 100 97 34 58 34 118 97 108 117 101 115 34 44 34 101 120 112 114 34 58 123 34 103 101 116 34 58 123 34 115 101 108 101 99 116 34 58 49 44 34 102 114 111 109 34 58 123 34 118 97 114 34 58 34 118 97 108 117 101 115 34 125 125 125 125 44 34 99 111 108 108 101 99 116 105 111 110 34 58 123 34 118 97 114 34 58 34 112 97 103 101 34 125 125 125 125]} data:map[gql:not included for brevity] name:sort_by_title ref:{sort_by_title 0xc000176bd0 0xc000176bd0 <nil>} ts:1627945509210000]
System.out.println(
    client.query(
        Update(
            Function("sort_by_title"),
            Obj(
                "body", Query(
                    Lambda(
                        Arr(Value("size"), Value("after"), Value("before")),
                        Let(
                            "match", Match(Index("all_posts_sorted_by_title")),
                            "page", If(
                                Equals(Var("before"), null),
                                If(
                                    Equals(Var("after"), null),
                                    Paginate(Var("match"))
                                        .size(Var("size")),
                                    Paginate(Var("match"))
                                        .size(Var("size")).after(Var("after"))
                                ),
                                Paginate(Var("match"))
                                    .size(Var("size")).before(Var("before"))
                            )
                        ).in(
                            Map(
                                Var("page"),
                                Lambda(
                                    Value("values"),
                                    Get(Select(Value(1), Var("values")))
                                )
                            )
                        )
                    )
                )
            )
        )
    ).get()
);
{ref: ref(id = "sort_by_title", collection = ref(id = "functions")), ts: 1627945236890000, name: "sort_by_title", body: QueryV({api_version=4, lambda=[size, after, before], expr={let={match={match={index=all_posts_sorted_by_title}}, page={if={equals=[{var=before}, null]}, then={if={equals=[{var=after}, null]}, then={paginate={var=match}, size={var=size}}, else={paginate={var=match}, after={var=after}, size={var=size}}}, else={paginate={var=match}, before={var=before}, size={var=size}}}}, in={map={lambda=values, expr={get={select=1, from={var=values}}}}, collection={var=page}}}}), data: {gql: "not included for brevity"}}
client.query(
  q.Update(
    q.Function('sort_by_title'),
    {
      body: q.Query(
        q.Lambda(
          ['size', 'after', 'before'],
          q.Let(
            {
              match: q.Match(q.Index('all_posts_sorted_by_title')),
              page: q.If(
                q.Equals(q.Var('before'), null),
                q.If(
                  q.Equals(q.Var('after'), null),
                  q.Paginate(
                    q.Var('match'),
                    { size: q.Var('size') },
                  ),
                  q.Paginate(
                    q.Var('match'),
                    { size: q.Var('size'), after: q.Var('after') },
                  )
                ),
                q.Paginate(
                  q.Var('match'),
                  { size: q.Var('size'), before: q.Var('before') },
                ),
              ),
            },
            q.Map(
              q.Var('page'),
              q.Lambda(
                'values',
                q.Get(q.Select(1, q.Var('values')))
              )
            )
          )
        )
      ),
    }
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Function("sort_by_title"),
  ts: 1626882814550000,
  name: 'sort_by_title',
  data: { gql: "not included for brevity" },
  body: Query(Lambda(["size", "after", "before"], Let({"match": Match(Index("all_posts_sorted_by_title")), "page": If(Equals(Var("before"), null), If(Equals(Var("after"), null), Paginate(Var("match"), {"size": Var("size")}), Paginate(Var("match"), {"after": Var("after"), "size": Var("size")})), Paginate(Var("match"), {"before": Var("before"), "size": Var("size")}))}, Map(Var("page"), Lambda("values", Get(Select(1, Var("values"))))))))
}
result = client.query(
  q.update(
    q.function("sort_by_title"),
    {
      "body": q.query(
        q.lambda_(
          ["size", "after", "before"],
          q.let(
            {
              "match": q.match(q.index("all_posts_sorted_by_title")),
              "page": q.if_(
                q.equals(q.var("before"), None),
                q.if_(
                  q.equals(q.var("after"), None),
                  q.paginate(
                    q.var("match"),
                    { "size": q.var("size") }
                  ),
                  q.paginate(
                    q.var("match"),
                    { "size": q.var("size"), "after": q.var("after") }
                  )
                ),
                q.paginate(
                  q.var("match"),
                  { "size": q.var("size"), "before": q.var("before") }
                ),
              )
            },
            q.map_(
              q.lambda_("values", q.get(q.select(1, q.var("values")))),
              q.var("page")
            )
          )
        )
      )
    }
  )
)
print(result)
{'ref': Ref(id=sort_by_title, collection=Ref(id=functions)), 'ts': 1627944162430000, 'name': 'sort_by_title', 'body': Query({'api_version': '4', 'lambda': ['size', 'after', 'before'], 'expr': {'let': {'match': {'match': {'index': 'all_posts_sorted_by_title'}}, 'page': {'if': {'equals': [{'var': 'before'}, None]}, 'then': {'if': {'equals': [{'var': 'after'}, None]}, 'then': {'paginate': {'var': 'match'}, 'size': {'object': {'size': {'var': 'size'}}}}, 'else': {'paginate': {'var': 'match'}, 'size': {'object': {'size': {'var': 'size'}, 'after': {'var': 'after'}}}}}, 'else': {'paginate': {'var': 'match'}, 'size': {'object': {'size': {'var': 'size'}, 'before': {'var': 'before'}}}}}}, 'in': {'map': {'lambda': 'values', 'expr': {'get': {'select': 1, 'from': {'var': 'values'}}}}, 'collection': {'var': 'page'}}}}), 'data': {'gql': 'not included for brevity'}}
Update(
  Function("sort_by_title"),
  {
    body: Query(
      Lambda(
        ["size", "after", "before"],
        Let(
          {
            match: Match(Index("all_posts_sorted_by_title")),
            page: If(
              Equals(Var("before"), null),
              If(
                Equals(Var("after"), null),
                Paginate(
                  Var("match"),
                  { size: Var("size") }
                ),
                Paginate(
                  Var("match"),
                  { size: Var("size"), after: Var("after") }
                )
              ),
              Paginate(
                Var("match"),
                { size: Var("size"), before: Var("before") }
              ),
            )
          },
          Map(
            Var("page"),
            Lambda(
              "values",
              Get(Select(1, Var("values")))
            )
          )
        )
      )
    )
  }
)
{
  ref: Function("sort_by_title"),
  ts: 1635289852000000,
  name: 'sort_by_title',
  body: Query(Lambda(["size", "after", "before"], Let({match: Match(Index("all_posts_sorted_by_title")), page: If(Equals(Var("before"), null), If(Equals(Var("after"), null), Paginate(Var("match"), {size: Var("size")}), Paginate(Var("match"), {after: Var("after"), size: Var("size")})), Paginate(Var("match"), {before: Var("before"), size: Var("size")}))}, Map(Var("page"), Lambda("values", Get(Select(1, Var("values")))))))),
  data: { gql: 'not included for brevity' }
}
Query metrics:
  •    bytesIn:  637

  •   bytesOut:  771

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  274

  • writeBytes:  686

  •  queryTime: 23ms

  •    retries:    0

Now you have a function which gets results from the all_posts_sorted_by_title index and arranges them in alphabetical order.

The following GraphQL query uses the allPostsSortedByTitle query specified in the schema:

query FindAllPosts {
  allPostsSortedByTitle {
    data {
      title
      content
    }
  }
}

The above query generates the following results, in alphabetical order by title:

{
  "data": {
    "allPostsSortedByTitle": {
      "data": [
        {
          "title": "my first post",
          "content": "placeholder content"
        },
        {
          "title": "my second post",
          "content": "more placeholder content"
        },
        {
          "title": "my third post",
          "content": "even more placeholder content"
        }
      ]
    }
  }
}

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!