To get a single document in Fauna we do:
q.Get(q.Ref(q.Collection('posts'), '192903209792046592'))
And we get something like:
{
data: {
title: 'Lorem ipsum',
authorId: '892943607792046595'
}
}
Is it possible to instead get the post and the author in the same query?
Something like this:
{
data: {
title: 'Lorem ipsum',
author: {
name: 'John Doe'
}
}
}
Since this is a common question I'm going to expand it and give you all info that you should need to get started. I recently wrote an example that will soon also exemplify this in detail.
I'm going to construct the query step by step to be as education as possible Let's say.. we write a twitter like application and want to retrieve tweets. The first thing we would do is just get a list of tweets.
Paginate(Documents(Collection('fweets')))
which returns a list of references
Paginate(Documents(Index('some_index')))
which returns all values that you selected when you created the index, something like: [[value1, value2], ...]
This will return a Page of references, or essentially a list of references.
Then you do what you did in your question, you Get the reference by mapping over it with Map (and Map will be your workhorse to answer your question as we go further)
Map(
Paginate(Documents(Collection('fweets'))),
Lambda('ref', Var('ref'))
)
You can use the exact same technique as we did to get the references, map over the document. Only now we would do the Get on the references that point to other collections. Imagine I have an Author in each of my Tweets, let us get that author. We are going to user Let to structure our query and go step by step Let's first restructure our query with Let
Map(
Paginate(Documents(Collection('fweets'))),
// and in this function, the magic will happen, for now we just return the tweet.
Lambda('f',
Let({
fweet: Get(Var('f'))
},
Var('fweet')
)
)
)
We'll now add one line to get the authors.
Map(
Paginate(Documents(Collection('fweets'))),
// and in this function, the magic will happen
Lambda('f',
Let({
fweet: Get(Var('f')),
author: Get(Select(['data', 'author'], Var('fweet'))), // we get the author reference
},
// And now we return a nested doc
{
fweet: Var('fweet'),
author: Var('author')
}
)
)
)
This would return:
[{
"fweet": {
< your tweet data >
},
"author": {
< your author data >
}
}, ... ]
And now that we have this structure adding something extra is easy. Imagine we also have an 'asset' tweet linked to the tweet for which we store the ref in tweets
Map(
Paginate(Documents(Collection('fweets'))),
Lambda('f',
Let({
fweet: Get(Var('f')),
author: Get(Select(['data', 'author'], Var('fweet'))),
asset: Get(Select(['data', 'asset'], Var('fweet')))
},
// And now we return a nested doc
{
fweet: Var('fweet'),
author: Var('author'),
asset: Var('asset'),
}
)
)
)
Of course.. what if the thing we want to fetch is not a stored reference but we want to join on a property? So imagine we have multiple comments on a tweet that we want to fetch? That is where indexes come in!
Map(
Paginate(Documents(Collection('fweets'))),
Lambda('f',
Let({
fweet: Get(Var('f')),
author: Get(Select(['data', 'author'], Var('fweet'))),
asset: Get(Select(['data', 'asset'], Var('fweet'))),
comments: Map(
Paginate(Match(Index('comments_by_fweet_ordered'), Var('f'))),
Lambda(
// my index has two values, I only need the comment reference but adding the ts makes them appear in order!
['ts', 'commentref'],
Get(Var('commentref'))
)
)
},
// And now we return a nested doc
{
fweet: Var('fweet'),
author: Var('author'),
asset: Var('asset'),
comments: Var('comments')
}
)
)
)
and just like that.. you can add complexity gradually and do really complex queries. The query in my app continues like that to get things such as tweet statistics or even the original tweet if its a retweet There is actually little you can't do in FQL :)
So first, it's best to store refs in documents and not ids.
When storing the example post in the question it should be something like this (with the JS driver):
{
title: 'Lorem ipsum',
authorRef: q.Ref(q.Collection("authors"), "1234556456858234")
}
Then to get the post with the nested author you need to create custom objects with Let
.
It would be something like this (again with the JS driver):
q.Let(
{
postDoc: q.Get(q.Ref(Collection('posts'), '192903209792046592')),
authorRef: q.Select(['data', 'authorRef'], q.Var('postDoc')),
authorDoc: q.Get(q.Var('authorRef')),
},
{
title: q.Select(['data', 'title'], q.Var('postDoc')),
author: {
name: q.Select(['data', 'name'], q.Var('authorDoc')),
}
}
)
Which would return:
{
title: 'Lorem ipsum',
author: {
name: 'John Doe'
}
}
Another option could be to simply return the two full documents side-by-side:
q.Let(
{
postDoc: q.Get(q.Ref(Collection('posts'), '192903209792046592')),
authorRef: q.Select(['data', 'authorRef'], q.Var('postDoc'))
},
{
postDoc: q.Var('postDoc'),
authorDoc: q.Get(q.Var('authorRef')),
}
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With