Overview
This is a cookbook of examples that demonstrate various Fauna queries, including Databases, Keys, Collections, User-defined functions, and Indexes.
Databases
Reading or writing database definitions requires an admin key.
Create a database
var adminClient = new faunadb.Client({
secret: adminKey
})
adminClient.query(
q.CreateDatabase({ name: 'annuvin' })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=annuvin, collection=Ref(id=databases)),
ts: 1520223296616581,
name: 'annuvin' }
Paginate all databases
adminClient.query(
q.Paginate(q.Databases())
)
.then((ret) => console.log(ret))
{ data:
[ Ref(id=prydain, collection=Ref(id=databases)),
Ref(id=caledonia, collection=Ref(id=databases)),
Ref(id=annuvin, collection=Ref(id=databases)) ] }
Get a database
adminClient.query(
q.Get(q.Database('annuvin'))
)
.then((ret) => console.log(ret))
{ ref: Ref(id=annuvin, collection=Ref(id=databases)),
ts: 1520223296616581,
name: 'annuvin' }
Rename a database
adminClient.query(
q.Update(q.Database('annuvin'), { name: 'llyr' })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=llyr, collection=Ref(id=databases)),
ts: 1520223296736896,
name: 'llyr' }
Keys
Reading or writing keys requires an admin key.
Create a key
var adminClient = new faunadb.Client({
secret: adminKey
})
adminClient.query(
q.CreateKey({
database: q.Database('caledonia'),
role: 'server',
})
)
.then((ret) => console.log(ret))
{ ref: Ref(id=200669511526908416, collection=Ref(id=keys)),
ts: 1527632209151020,
database: Ref(id=caledonia, collection=Ref(id=databases)),
role: 'server',
secret: 'fnACyOvbh9ACAOtRQDQSefokn4iuMf9zn_FakPAx',
hashed_secret: '$2a$05$dszFRic/tkKzsG0rrUj3Fu7.nSoYuJL7BdnHXepx4XTzfcWtVEZRC' }
Paginate all keys
adminClient.query(q.Paginate(q.Keys()))
.then((ret) => console.log(ret))
{ data:
[ Ref(id=192900703858983424, collection=Ref(id=keys)),
Ref(id=192900703888343552, collection=Ref(id=keys)) ] }
Get a key
adminClient.query(
q.Get(q.Ref(q.Keys(), '192900703888343552'))
)
.then((ret) => console.log(ret))
{ ref: Ref(id=192900703888343552, collection=Ref(id=keys)),
ts: 1527632209151020,
database: Ref(id=caledonia, collection=Ref(id=databases)),
role: 'server',
hashed_secret: '$2a$05$dszFRic/tkKzsG0rrUj3Fu7.nSoYuJL7BdnHXepx4XTzfcWtVEZRC' }
Delete a key
adminClient.query(q.Delete(q.Ref(q.Keys(), '192900703858983424')))
.then((ret) => console.log(ret))
{ ref: Ref(id=192900703858983424, collection=Ref(id=keys)),
ts: 1527632209151020,
database: Ref(id=caledonia, collection=Ref(id=databases)),
role: 'server',
hashed_secret: '$2a$05$dszFRic/tkKzsG0rrUj3Fu7.nSoYuJL7BdnHXepx4XTzfcWtVEZRC' }
Collections
Reading or writing collections requires a server key.
Create a collection
client.query(
q.CreateCollection({ name: 'spells' })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells, collection=Ref(id=collections))
ts: 1520223297210738,
history_days: 30,
name: 'spells' }
Paginate all collections
client.query(
q.Paginate(q.Collections())
)
.then((ret) => console.log(ret))
{ data:
[ Ref(id=decrepit_huts, collection=Ref(id=collections)),
Ref(id=spellbooks, collection=Ref(id=collections)),
Ref(id=characters, collection=Ref(id=collections)),
Ref(id=spells, collection=Ref(id=collections)) ] }
Get a collection
client.query(
q.Get(q.Collection('spells'))
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells, collection=Ref(id=collections))
ts: 1520223297210738,
history_days: 30,
name: 'spells' }
Rename a collection
client.query(
q.Update(
q.Collection('decrepit_huts'),
{ name: 'dilapidated_huts' },
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=dilapidated_huts, collection=Ref(id=collections)),
ts: 1520223297276685,
history_days: 30,
name: 'dilapidated_huts' }
Delete a collection
client.query(
q.Delete(q.Collection('dilapidated_huts'))
)
.then((ret) => console.log(ret))
{ ref: Ref(id=dilapidated_huts, collection=Ref(id=collections)),
ts: 1520223297276685,
history_days: 30,
name: 'dilapidated_huts' }
Create a document in a collection
client.query(
q.Create(
q.Collection('spells'),
{
data: {
name: 'Fire Beak',
element: ['air', 'fire'],
},
},
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=200669801878651393, collection=Ref(id=spells, collection=Ref(id=collections))),
ts: 1527632486061801,
data: { name: 'Fire Beak', element: [ 'air', 'fire' ] } }
User-defined functions
Creating and updating a function requires:
-
An admin or server key. See Keys and Permissions for details.
-
The privilege to create/update a function, granted in an Attribute-based access control (ABAC) role.
Create a user-defined function
Let’s create a function to allow our authors to create new blog posts. We’ll set it up to allow the author to provide a title and body. This way authors are constrained by what data they can store on a post document.
client.query(
q.CreateFunction({
name: 'create_entry',
body: q.Query(
q.Lambda(
['title', 'body'],
q.Create(
q.Collection('posts'),
{
data: {
title: q.Var('title'),
body: q.Var('body'),
},
},
),
)
),
permissions: { call: 'public' },
role: 'server',
})
)
.then((ret) => console.log(ret))
{ ref: Ref(id=create_entry, collection=Ref(id=functions)),
ts: 1527633126019327,
name: 'create_entry',
body: Query("[object Object]"),
permissions: { call: 'public' },
role: 'server' }
Create credentials, login, and call a user-defined function
Now we can create a new author and use their credentials.
client.query(
q.Create(
q.Collection('authors'),
{
data: { name: 'Patty Smith' },
credentials: { password: '123456' },
}
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=192900738957967872, collection=Ref(id=authors, collection=Ref(id=collections))),
ts: 1527633688652829,
data: { name: 'Patty Smith' } }
client.query(
q.Login(
q.Ref(q.Collection('authors'), '192900738957967872'),
{ password: '123456' }
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=192900739000959488, collection=Ref(id=tokens)),
ts: 1520223330375483,
document: Ref(id=192900738957967872, collection=Ref(id=authors, collection=Ref(id=collections))),
secret: 'fnECrVIzORACAAKtUir3AAIAq-5ZBLtxuAEhY0cju64xXi_HUhg' }
The value in the secret
field is the authentication token representing
the author that we just created. A new client connection object needs to
be created using this secret, so that subsequent queries are performed
as the author. See the User
authentication tutorial for more details.
The following query calls the create_entry
function that we created
earlier:
client.query(
q.Call(
q.Function('create_entry'),
[ 'First Post Title', 'This is my first blog post!' ],
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=192900739108962816, collection=Ref(id=posts, collection=Ref(id=collections))),
ts: 1520223330453871,
data:
{ title: 'First Post Title',
body: 'This is my first blog post!' } }
Indexes
Creating and updating an index requires a server key.
Create an index
Let’s create an index to list all spells' names:
client.query(
q.CreateIndex(
{
name: 'all_spell_names',
source: q.Collection('spells'),
values: [{ field: ['data', 'name'] }],
},
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=all_spell_names, collection=Ref(id=indexes)),
ts: 1527625252080602,
active: false,
partitions: 8,
name: 'all_spell_names',
source: Ref(id=spells, collection=Ref(id=collections)),
values: [ { field: [Array] } ] }
It’s also possible to specify the terms that are going to be used to locate entries on the index. In this case, we’ll create an index that allow us to find spells by their elements:
client.query(
q.CreateIndex(
{ name: 'spells_by_element_with_name',
source: q.Collection('spells'),
terms: [{ field: ['data', 'element'] }],
values: [{ field: ['data', 'name'] }],
},
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells_by_element_with_name, collection=Ref(id=indexes)),
ts: 1527625636424120,
active: false,
partitions: 1,
name: 'spells_by_element_with_name',
source: Ref(id=spells, collection=Ref(id=collections)),
terms: [ { field: [Array] } ],
values: [ { field: [Array] } ] }
Indexes can also cover multiple values, which affect the order of their results. See the ordering section for more information.
The length of the field values specified for the terms or
values fields must not exceed 32k bytes. The maximum size of an
index entry, which is comprised of the terms and values
content (and some overhead to distinguish multiple fields),
must not exceed 64k bytes. If an index entry is too large, the
query that created/updated the index entry fails.
|
Here is an example of an index that allows you to search for a spell by
its element and get back both the spell name and ref
:
client.query(
q.CreateIndex(
{
name: 'spells_with_ref_by_element_name',
source: q.Collection('spells'),
terms: [{ field: ['data', 'element'] }],
values: [{ field: ['data', 'name'] }, { field: ['ref'] }],
},
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells_with_ref_by_element_name, collection=Ref(id=indexes)),
ts: 1527625316880457,
active: false,
partitions: 1,
name: 'spells_with_ref_by_element_name',
source: Ref(id=spells, collection=Ref(id=collections)),
terms: [ { field: [Array] } ],
values: [ { field: [Array] }, { field: [Array] } ] }
Paginate all indexes
client.query(q.Paginate(q.Indexes()))
.then((ret) => console.log(ret))
{ data: [
Ref(id=all_spells, collection=Ref(id=indexes)),
Ref(id=spells_by_name, collection=Ref(id=indexes)),
Ref(id=spells_by_element, collection=Ref(id=indexes)),
Ref(id=spells_by_element_and_name, collection=Ref(id=indexes)),
Ref(id=spellbooks_by_owner, collection=Ref(id=indexes)),
Ref(id=spells_by_spellbook, collection=Ref(id=indexes)),
Ref(id=all_spell_names, collection=Ref(id=indexes)),
Ref(id=spells_by_element_with_name, collection=Ref(id=indexes)),
Ref(id=spells_with_ref_by_element_name, collection=Ref(id=indexes)),
Ref(id=latest_spells_by_element, collection=Ref(id=indexes)) ] }
Get an index
client.query(
q.Get(q.Index('spells_by_element_with_name'))
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells_by_element_with_name, collection=Ref(id=indexes)),
ts: 1527625636424120,
active: false,
partitions: 1,
name: 'spells_by_element_with_name',
source: Ref(id=spells, collection=Ref(id=collections)),
terms: [ { field: [Array] } ],
values: [ { field: [Array] } ] }
Query an index
Indexes created without specifying the terms
field return all indexed
values
on the indexed collection.
client.query(
q.Paginate(q.Match(q.Index('all_spell_names')))
)
.then((ret) => console.log(ret))
{ data: [ "Fire Beak", "Hippo's Wallow", "Water Dragon's Claw" ] }
For indexes where the terms
field was defined, entries are located
using the indexed terms.
client.query(
q.Paginate(
q.Match(q.Index('spells_by_element_with_name'), 'fire')
)
)
.then((ret) => console.log(ret))
{ data: [ "Fire Beak", "Water Dragon's Claw" ] }
Indexes return all their values field values. Here we’re searching for
all of the spells with element fire
, using an index that returns both
the spell name and its reference:
client.query(
q.Paginate(
q.Match(q.Index('spells_with_ref_by_element_name'), 'fire')
)
)
.then((ret) => console.log(ret))
{ data: [
[ "Fire Beak",
Ref(id=192900707573039616, collection=Ref(id=spells, collection=Ref(id=collections))) ],
[ "Water Dragon's Claw",
Ref(id=192900707595059712, collection=Ref(id=spells, collection=Ref(id=collections))) ] }
Indexes with ordering and transformations
Let’s create an index to return the latest spells updated. To do this we will create an index which is sorted reversely by each spell’s last updated timestamp.
We’ll specify terms on this index so we can find spells by element.
We’ll also use the casefold
function as a transformation to the
specified term which will make the entries lowercase in the index.
client.query(
q.CreateIndex(
{
name: 'latest_spells_by_element',
source: q.Collection('spells'),
terms: [{ field: ['data', 'element'], transform: 'casefold' }],
values: [
{ field: ['ts'], reverse: true },
{ field: ['data', 'name'] },
{ field: ['ref'] },
],
},
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=latest_spells_by_element, collection=Ref(id=indexes)),
ts: 1527625370644230,
active: false,
partitions: 1,
name: 'latest_spells_by_element',
source: Ref(id=spells, collection=Ref(id=collections)),
terms: [ { field: [Array], transform: 'casefold' } ],
values:
[ { field: [Array], reverse: true },
{ field: [Array] },
{ field: [Array] } ] }
When querying the index, you can use the casefold
function to convert
the query terms to lowercase. Using casefold
in a query to an index
configured with casefold
essentially makes a case-insensitive query.
client.query(
q.Paginate(
q.Match(
q.Index('latest_spells_by_element'),
q.Casefold('FIRE'),
)
)
)
.then((ret) => console.log(ret))
{ data:
[ [ 1520223300442183,
"Water Dragon's Claw",
Ref(id=192900707595059712, collection=Ref(id=spells, collection=Ref(id=collections))) ],
[ [ 1520223300419731
"Fire Beak",
Ref(id=192900707573039616, collection=Ref(id=spells, collection=Ref(id=collections))) ] ] }
Since timestamp was added to the index to facilitate sorting, it might
not be required for the query to return timestamp values to the
application layer. In that case, it’s possible to use the map
function
to extract only the desired fields.
client.query(
q.Map(
q.Paginate(
q.Match(
q.Index('latest_spells_by_element'),
q.Casefold('FIRE'),
)
),
q.Lambda(['_', 'name', 'ref'], [q.Var('name'), q.Var('ref')]),
)
)
.then((ret) => console.log(ret))
{ data:
[ [ "Water Dragon's Claw",
Ref(id=192900707595059712, collection=Ref(id=spells, collection=Ref(id=collections))) ],
[ "Fire Beak",
Ref(id=192900707573039616, collection=Ref(id=spells, collection=Ref(id=collections))) ] ] }
Update an index
It is disallowed to update an index in a way that changes its shape.
This means no changing the source or the terms. You can, however, change
the name of an index or its uniqueness. If you update the unique
field, it does not remove existing duplicated items from the index.
client.query(
q.Update(
q.Index('spells_by_element_with_name'),
{ name: 'spells_by_kind' },
)
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells_by_kind, collection=Ref(id=indexes)),
ts: 1527630282459547,
active: true,
partitions: 1,
name: 'spells_by_kind',
source: Ref(id=spells, collection=Ref(id=collections)),
terms: [ { field: [Array] } ],
values: [ { field: [Array] } ] }
Delete an index
client.query(
q.Delete(q.Index('spells_by_kind'))
)
.then((ret) => console.log(ret))
{ ref: Ref(id=spells_by_kind, collection=Ref(id=indexes)),
ts: 1527625346513000,
active: true,
partitions: 1,
name: 'spells_by_kind',
source: Ref(id=spells, collection=Ref(id=collections)),
terms: [ { field: [Array] } ],
values: [ { field: [Array] }, { field: [Array] } ] }
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!