Querying: Basics
-
Queries can either be written in the Session with LINQ syntax or in the Studio with RQL.
There are examples for both below. -
Indexes are used by RavenDB to satisfy queries.
-
To accelerate queries, RavenDB indexes can process various calculations, filters and conversions behind the scenes so that the data is already processed and ready for queries.
Indexes keep the processed data in a separate storage so that the raw data isn't affected.
Furthermore, indexes only scan and process the entire specified dataset once.
After the initial scan, they only need to process specific data as it is modified, added or deleted.- For queries to use an index that has already processed the data, the index must be called in the query.
-
In this page:
Query-Flow
Queries in RavenDB can be defined in Studio with RQL, our query language, or in the Session with LINQ syntax. Each query must match an index in order to return the results. If no index exists to satisfy the query and if a specific index isn't specified, an Auto-Index will be created automatically.
The full query flow is as follows:
-
from index | collection
- First step. When a query is issued, it locates the appropriate index. If our query specifies that index, the task is simple - use this index. Otherwise, a query analysis takes place and an auto-index is created if no auto-index can already satisfy the query.
-
where
- When we have our index, we scan it for records that match the query predicate.
-
load
- If a query contains a projection that requires any document loads to be processed, they are done just before projection is executed.
-
select
-
From each record, the server extracts the appropriate fields. It always extracts the
id()
field (stored by default). -
If a query is not a projection query, then we load a document from storage. Otherwise, if we stored all requested fields in the index, we use them and continue. If not, the document is loaded from storage and the missing fields are fetched from it.
-
If a query indicates that projection should be used, then all results that were not filtered out are processed by that projection. Fields defined in the projection are extracted from the index (if stored).
-
-
include
- If any includes are defined, then the query also extracts data from potential related documents to include with the results.
-
(LINQ syntax)
ToList
orToListAsync
- Return results.
Querying Using LINQ
RavenDB Client supports querying using LINQ. This functionality can be accessed using the session Query
method, and is the most common and basic method for querying the database.
Example I - Querying an Entire Collection
Let's execute our first query and return all the employees from the Northwind database.
To do that, we need to have a document store with an open session,
and specify a collection
that we want to query (in our case Employees
) by passing Employee
as a generic parameter to the Query
method:
// load all entities from 'Employees' collection
IList<Employee> results = session
.Query<Employee>()
.ToList(); // send query
// load all entities from 'Employees' collection
IList<Employee> results = await asyncSession
.Query<Employee>()
.ToListAsync(); // send query
from Employees
By specifying Employee
as a type parameter, we are also defining a result type.
Example II - Filtering
To filter the results, use the suitable LINQ method, like Where
:
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
IList<Employee> results = session
.Query<Employee>()
.Where(x => x.FirstName == "Robert")
.ToList(); // send query
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
IList<Employee> results = await asyncSession
.Query<Employee>()
.Where(x => x.FirstName == "Robert")
.ToListAsync(); // send query
from Employees
where FirstName = 'Robert'
// load up entity from 'Employees' collection
// with ID matching 'employees/1-A'
Employee result = session
.Query<Employee>()
.Where(x => x.Id == "employees/1-A")
.FirstOrDefault(); // send query
// load up entity from 'Employees' collection
// with ID matching 'employees/1-A'
Employee result = await asyncSession
.Query<Employee>()
.Where(x => x.Id == "employees/1-A")
.FirstOrDefaultAsync(); // send query
from Employees
where id() = 'employees/1-A'
You can read more about filtering here.
Example III - Paging
Paging is very simple. The methods Take
and Skip
can be used:
// load up to 10 entities from 'Products' collection
// where there are more than 10 units in stock
// skip first 5 results
IList<Product> results = session
.Query<Product>()
.Where(x => x.UnitsInStock > 10)
.Skip(5)
.Take(10)
.ToList(); // send query
// load up to 10 entities from 'Products' collection
// where there are more than 10 units in stock
// skip first 5 results
IList<Product> results = await asyncSession
.Query<Product>()
.Where(x => x.UnitsInStock > 10)
.Skip(5)
.Take(10)
.ToListAsync(); // send query
You can read more about paging here.
Example IV - Querying a Specified Index
In the above examples, we did not specify an index that we want to query. If you don't specify an index, RavenDB will look for an appropriate auto-index or create a new one. You can read more about creating indexes here.
To query a static index, you must specify the index in the query definition.
In order to specify an index, we need to pass it as a second generic parameter to the Query
method
or pass the index name as a parameter.
In this example, the index name is Employees_ByFirstName
if written for LINQ or Employees/ByFirstName
if written for RQL.
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
// using 'Employees/ByFirstName' index
IList<Employee> results = session
.Query<Employee, Employees_ByFirstName>()
.Where(x => x.FirstName == "Robert")
.ToList(); // send query
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
// using 'Employees/ByFirstName' index
IList<Employee> results = await asyncSession
.Query<Employee, Employees_ByFirstName>()
.Where(x => x.FirstName == "Robert")
.ToListAsync(); // send query
from index 'Employees/ByFirstName'
where FirstName = 'Robert'
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
// using 'Employees/ByFirstName' index
IList<Employee> results = session
.Query<Employee>("Employees/ByFirstName")
.Where(x => x.FirstName == "Robert")
.ToList(); // send query
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
// using 'Employees/ByFirstName' index
IList<Employee> results = await asyncSession
.Query<Employee>("Employees/ByFirstName")
.Where(x => x.FirstName == "Robert")
.ToListAsync(); // send query
from index 'Employees/ByFirstName'
where FirstName = 'Robert'
Remember
If you are filtering by fields that are not present in an index, an exception will be thrown.
Low-Level Query Access
To take full control over your queries, we introduced a DocumentQuery
method that is available in advanced session operations. It is a low-level access to the querying mechanism the user can take advantage of to shape queries according to his needs.
Example
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
// using 'Employees/ByFirstName' index
IList<Employee> results = session
.Advanced
.DocumentQuery<Employee, Employees_ByFirstName>()
.WhereEquals(x => x.FirstName, "Robert")
.ToList(); // send query
// load all entities from 'Employees' collection
// where 'FirstName' is 'Robert'
// using 'Employees/ByFirstName' index
IList<Employee> results = await asyncSession
.Advanced
.AsyncDocumentQuery<Employee, Employees_ByFirstName>()
.WhereEquals(x => x.FirstName, "Robert")
.ToListAsync(); // send query
from index 'Employees/ByFirstName'
where FirstName = 'Robert'
Remarks
Information
You can check the API reference for the DocumentQuery
here.
Information
There are some differences between Query
and DocumentQuery
. They are described in this article.