FQL v4 will be decommissioned on June 30, 2025. Ensure that you complete your migration from FQL v4 to FQL v10 by that date. Fauna accounts created after August 21, 2024 must use FQL v10. These accounts will not be able to run FQL v4 queries or access the v4 Dashboard. For more details, see the v4 EOL announcement and migration guide. Contact support@fauna.com with any questions. |
Social graph
Social graphs are a common feature of many modern mobile, gaming, and web applications. This page will walk you through the process of creating a simple social graph. The example we’ll build here can support features such as timelines, suggested followers, and Erdős numbers.
This tutorial assumes that you have completed the Dashboard quick start. |
You can also complete this tutorial with the Fauna Shell. All the commands work the same way in the Dashboard Shell and the command-line Fauna Shell. |
Step 1: Open the Fauna Dashboard
Log in to the Fauna Dashboard and click the database
my_db
in your list of databases.
Step 3: Create a collection and index to represent people
The first collection that we need to create is people
, to represent
the users in our social graph. Users could be players in a game,
subscribers to an author’s articles, or coworkers in a professional
network.
CreateCollection({ name: "people" })
Then, we need to create an index on peoples' names. The index allows us
to refer to people with names like "alice" rather than refs like
Ref(Collection("people"), 1)
. Also, let’s use the unique constraint to
ensure that multiple users don’t have the same name.
CreateIndex({
name: "people_by_name",
source: Collection("people"),
terms: [{ field: ["data", "name"] }],
unique: true
})
Step 4: Create a collection and index to represent relationships
The connection between people is called a "relationship". Relationships are directional: a person may follow another person without requiring the second person to reciprocate the relationship. Let’s call the first person a "follower" of the "followee".
CreateCollection({ name: "relationships" })
Then, we need to create an index on the relationships
collection which
is the core of our social graph. This index allows us to easily answer
questions such as "who follows person A?" or "who follows person A, but
not person B?"
CreateIndex({
name: "followers_by_followee",
source: Collection("relationships"),
terms: [{ field: ["data", "followee"] }],
values: [{ field: ["data", "follower"] }]
})
Step 5: Populate the graph
Now that we have collections representing the people in our graph
(people
) and their relationships to each other (relationships
), we
can begin populating the graph.
First, let’s create four users: Alice, Bob, Carol, and Dave. Notice that
we added an index on the field ["data", "name"]
for the people
collection. Later on, we can use that index to find the four people in
our graph.
Foreach(
["Alice", "Bob", "Carol", "Dave"],
Lambda("name",
Create(Collection("people"), { data: { name: Var("name") } })
)
)
In the following queries, we use this query pattern:
Get(Match(Index("people_by_name"), "Alice"))
This query uses the index people_by_name
to find the first person with
the name "Alice". We can be sure that the first person returned is the
one we’re looking for because of the uniqueness constraint we set when
we created the index.
The first relationship that we are going to create is between Alice and Bob. We’ll create a relationship with Alice as the follower, and Bob as the followee — in plain English, this says "Alice follows Bob."
Create(
Collection("relationships"),
{
data: {
follower: Select("ref", Get(Match(Index("people_by_name"), "Alice"))),
followee: Select("ref", Get(Match(Index("people_by_name"), "Bob")))
}
}
)
Next, let’s add a relationship in the other direction: Bob follows Alice.
Create(
Collection("relationships"),
{
data: {
follower: Select("ref", Get(Match(Index("people_by_name"), "Bob"))),
followee: Select("ref", Get(Match(Index("people_by_name"), "Alice")))
}
}
)
Let’s use the index people_by_name
to find all users named either
"Alice" or "Bob" and add a relationship to Carol — i.e. Carol follows
both Alice and Bob.
Let(
{
follower: Select("ref", Get(Match(Index("people_by_name"), "Carol")))
},
Foreach(
Paginate(
Union(
Match(Index("people_by_name"), "Alice"),
Match(Index("people_by_name"), "Bob")
)
),
Lambda("followee",
Create(
Collection("relationships"),
{
data: {
followee: Var("followee"),
follower: Var("follower")
}
}
)
)
)
)
Finally, let’s add a relationship meaning, "Dave follows Alice."
Create(
Collection("relationships"),
{
data: {
followee: Select("ref", Get(Match(Index("people_by_name"), "Alice"))),
follower: Select("ref", Get(Match(Index("people_by_name"), "Dave")))
}
}
)
Step 6: Explore the graph
We now have four users and relationships among them in our social graph:
-
Alice follows Bob,
-
Bob follows Alice,
-
Carol follows both Alice and Bob,
-
Dave follows Alice.
Using our index followers_by_followee
, we can now answer questions
about these relationships.
To find a person’s follower list, we use a Select
query to extract the
ref
of each follower, like this:
Select("ref", Get(Match(Index("people_by_name"), "Alice")))
Find Alice’s followers
We can use a ref
as the term in a Match
query on our relationship
index. This query returns all of the refs of Alice’s followers:
Paginate(Match(Index("people_by_name"), "Alice"))
The result should look something like this (the identifiers for your documents are distinct):
{ data: [ Ref(Collection("people"), "236350059641307648") ] }
Our application might be able to interpret refs, but they’re hard for
a human to comprehend, so let’s Map
over the set of refs, get each
person’s document, and select their name out of the document.
Map(
Paginate(Match(Index("people_by_name"), "Alice")),
Lambda("person",
Select(["data", "name"], Get(Var("person")))
)
)
The result should be:
{ data: [ 'Alice' ] }
Putting it all together gives us a human-friendly list of the names of Alice’s followers:
Map(
Paginate(
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Alice")))
)
),
Lambda("person",
Select(["data", "name"], Get(Var("person")))
)
)
The result should be similar to (the order might differ):
{ data: [ 'Bob', 'Dave', 'Carol' ] }
Find Alice’s OR Bob’s followers
Now that we’ve seen how to list a single person’s followers, we can use that knowledge to ask questions about the connections between follower lists.
For example, the union of the follower lists tells us who follows either person. Here, we ask who follows Alice or Bob:
Map(
Paginate(
Union(
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Alice")))
),
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Bob")))
)
)
),
Lambda("person",
Select(["data", "name"], Get(Var("person")))
)
)
The result should be similar to:
{ data: [ 'Alice', 'Bob', 'Dave', 'Carol' ] }
Find Alice’s AND Bob’s followers
The intersection of follower lists finds common followers among people. This query asks who follows Alice and Bob:
Map(
Paginate(
Intersection(
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Alice")))
),
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Bob")))
)
)
),
Lambda("person",
Select(["data", "name"], Get(Var("person")))
)
)
The result should be:
{ data: [ 'Carol' ] }
Find people who follow Alice, but NOT Bob
The difference of follower lists tells us who follows some people, but not others. This is how we ask for followers of Alice, but not Bob:
Map(
Paginate(
Difference(
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Alice")))
),
Match(
Index("followers_by_followee"),
Select("ref", Get(Match(Index("people_by_name"), "Bob")))
)
)
),
Lambda("person",
Select(["data", "name"], Get(Var("person")))
)
)
The result should be similar to:
{ data: [ 'Bob', 'Dave' ] }
Conclusion
In this tutorial, we have modeled a simple social graph using Fauna’s collections and indexes. We have used the query language to ask questions about the connections in the graph that are useful building blocks of social features in mobile apps, games, and web applications.
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!