Bindings
An index binding is a Lambda
function that computes the value for a field in a document while the
document is being indexed. Once defined, the binding can be applied to
the index’s terms
field, which allows you to search for computed
values, or the to index’s value
field, which allows you to return
computed values for index matches.
For example, the documents that we are indexing might include a timestamp. We might want to be able to search for documents by date, or we might want to return the day of week for index matches.
Bindings must be pure Lambda functions: they are not allowed to perform reads or writes. They are provided with a copy of the document to be indexed and must perform their operation on the document’s values. |
This tutorial demonstrates how to:
This tutorial assumes that you have successfully prepared your database by creating the necessary collections and documents. |
Let’s get started!
Create and use a binding
An index’s source
field defines one or more collections that should be
indexed. It is also used to define zero or more fields that have
associated binding functions. The binding functions compute the value
for the specified field while the document is being indexed.
Once a binding is defined, it must be used at least once in either the
index’s terms
or values
fields, or an error occurs: a binding
definition without use would cause unnecessary computation.
Let’s create an index for the People
collection (that we
created previously) that specifies a binding
function. Our binding function calculates the "rolodex letter" for
each person, which is the first letter of their last name.
CreateIndex({
name: "people_by_rolodex",
source: {
collection: Collection("People"),
fields: {
rolodex: Query(
Lambda(
"doc",
SubString(Select(["data", "last"], Var("doc")), 0, 1)
)
)
}
},
terms: [ { binding: "rolodex" }],
values: [
{ binding: "rolodex" },
{ field: ["data", "last"] },
{ field: ["ref"] }
]
})
The highlights of this query:
-
The index is called
people_by_rolodex
. -
The
source
field specifies that:-
Documents within the
People
collection should be indexed. -
The field
rolodex
(which does not have to exist in any document in the collection) has a Lambda function, -
The
Query
function defers execution of the Lambda function until a document needs to be indexed. -
The Lambda function itself accepts the current document as a variable called
doc
. Then it returns the first letter of the document’slast
field using theSubString
function. TheSelect
function is used to extract thelast
field from the document.
-
-
The
terms
field specifies that therolodex
binding’s result is to be used for searching this index. -
The
values
field specifies that therolodex
binding’s result is to be included for index matches, along with the document’s Reference.
When you run this query, the output should be similar to:
{ ref: Index("people_by_rolodex"),
ts: 1580948872710000,
active: true,
serialized: true,
name: 'people_by_rolodex',
source:
{ collection: Collection("People"),
fields:
{ rolodex:
Query(Lambda("doc", Substring(Select(["data", "last"], Var("doc")), 0, 1))) } },
terms: [ { binding: 'rolodex' } ],
values:
[ { binding: 'rolodex' },
{ field: [ 'data', 'last' ] },
{ field: [ 'ref' ] } ],
partitions: 1 }
Now we can find people by their "rolodex letter". Copy the following query, paste it into the Shell, and run it:
Paginate(Match(Index("people_by_rolodex"), "C"))
The output should be similar to:
{ data:
[ [ 'C', 'Cook', Ref(Collection("People"), "239535822978682381") ],
[ 'C', 'Cook', Ref(Collection("People"), "239535822978684429") ] ] }
Search for empty fields
One specific use of bindings that you might find to be very useful is to
locate empty fields. FaunaDB does not store empty fields, and you
cannot directly use null
with the
Match
function.
For example, with the following index:
CreateIndex({
name: "people_by_letter",
source: Collection("People"),
terms: [ { field: ["data", "letter"] } ]
})
We can search for people by letter:
Paginate(Match(Index("people_by_letter"), "A"))
{ data: [ Ref(Collection("People"), "240166254282805769") ] }
However, we cannot search for empty values using this index:
Paginate(Match(Index("people_by_letter"), null))
{ data: [] }
Why does this happen? When we pass null
to the
Match
function, the query compares
the indexed field values to null
. A field that is not set has no value
at all, whereas null
is a value: the two don’t match. Alternately, if
we do not pass a value to Match
, there is an empty comparison value to
compare with the index’s terms
fields. Since none of the indexed
entries is lacking a terms
field, none of the indexed entries matches
the empty comparison value.
Instead, we can create a binding that tells us when a field is unset:
CreateIndex({
name: "people_by_null_letter",
source: [{
collection: Collection("People"),
fields: {
null_letter: Query(
Lambda(
"doc",
Equals(Select(["data", "letter"], Var("doc"), null), null)
)
)
}
}],
terms: [ {binding: "null_letter"} ],
})
The highlights for this query:
-
The index is named
people_by_null_letter
. -
The
source
is thePeople
collection. -
A binding for the field
null_letter
is defined. -
The binding function:
-
Accepts the indexed document in the
doc
variable. -
Uses the
Select
function to pull out theletter
field, and to usenull
as the default value is the field is unset. -
Uses the
Equals
function to compare the value of theletter
field withnull
. The result of calling this function is the implicit return value for the binding function, and that value is eithertrue
orfalse
.
-
-
The
terms
field specifies the binding function’s return value as a search term.
With this index in place, we can now search for People
documents that
have unset letter
fields:
Map(Paginate(Match(Index("people_by_null_letter"), true)), Lambda("X", Get(Var("X"))))
{ data:
[ { ref: Ref(Collection("People"), "240166254282803721"),
ts: 1580867425600000,
data: { first: 'Leslie', last: 'Lamport' } } ] }
Conclusion
This tutorial has demonstrated how to define and use index bindings, which helps us achieve specific kinds of search results.
Was this article helpful?
We're sorry to hear that.
Tell us how we can improve!
documentation@fauna.com
Thank you for your feedback!