Project Index Query Results
-
This article provides examples of projecting query results when querying a static-index.
-
Prior to this article, please refer to Project query results - Overview
for general knowledge about Projections and dynamic-queries examples. -
In this page:
Select
Example I - Projecting individual fields of the document:
var projectedResults = session
// Query the index
.Query<Employees_ByNameAndTitle.IndexEntry, Employees_ByNameAndTitle>()
// Can filter by index-field, e.g. query only for sales representatives
.Where(x => x.Title == "sales representative")
// Call 'Select' to return only the first and last name per matching document
.Select(x => new
{
EmployeeFirstName = x.FirstName,
EmployeeLastName = x.LastName
})
.ToList();
// Each resulting object in the list is Not an 'Employee' entity,
// it is a new object containing ONLY the fields specified in the Select
// ('EmployeeFirstName' & 'EmployeeLastName').
var projectedResults = await asyncSession
// Query the index
.Query<Employees_ByNameAndTitle.IndexEntry, Employees_ByNameAndTitle>()
// Can filter by index-field, e.g. query only for sales representatives
.Where(x => x.Title == "sales representative")
// Call 'Select' to return only the first and last name per matching document
.Select(x => new
{
EmployeeFirstName = x.FirstName,
EmployeeLastName = x.LastName
})
.ToListAsync();
// Each resulting object in the list is Not an 'Employee' entity,
// it is a new object containing ONLY the fields specified in the Select
// ('EmployeeFirstName' & 'EmployeeLastName').
public class Employees_ByNameAndTitle : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
}
public Employees_ByNameAndTitle()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Title = employee.Title
};
}
}
from index "Employees/ByNameAndTitle"
where Title = "sales representative"
select FirstName, LastName
-
Since the index-fields in this example are not Stored in the index, and no projection behavior was defined,
resulting values will be retrieved from the matching Employee document in the storage. -
This behavior can be modified by setting the projection behavior used when querying a static-index.
Example II - Projecting stored fields:
var projectedResults = session
.Query<Employees_ByNameAndTitleWithStoredFields.IndexEntry,
Employees_ByNameAndTitleWithStoredFields>()
.Select(x => new
{
// Project fields 'FirstName' and 'LastName' which are stored in the index
EmployeeFirstName = x.FirstName,
EmployeeLastName = x.LastName
})
.ToList();
var projectedResults = await asyncSession
.Query<Employees_ByNameAndTitleWithStoredFields.IndexEntry,
Employees_ByNameAndTitleWithStoredFields>()
.Select(x => new
{
// Project fields 'FirstName' and 'LastName' which are stored in the index
EmployeeFirstName = x.FirstName,
EmployeeLastName = x.LastName
})
.ToListAsync();
public class Employees_ByNameAndTitleWithStoredFields : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
}
public Employees_ByNameAndTitleWithStoredFields()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Title = employee.Title
};
// Store some fields in the index:
Stores.Add(x => x.FirstName, FieldStorage.Yes);
Stores.Add(x => x.LastName, FieldStorage.Yes);
}
}
from index "Employees/ByNameAndTitleWithStoredFields"
select FirstName, LastName
-
In this example, all projected index-fields (
FirstName
andLastName
) are stored in the index,
so by default, the resulting values will come directly from the index and not from the Employee document in the storage. -
This behavior can be modified by setting the projection behavior used when querying a static-index.
Example III - Projecting arrays and objects:
var projectedResults = session
.Query<Orders_ByCompanyAndShipToAndLines.IndexEntry, Orders_ByCompanyAndShipToAndLines>()
.Where(x => x.Company == "companies/65-A")
.Select(x => new
{
// Retrieve a property from an object
ShipToCity = x.ShipTo.City,
// Retrieve all product names from the Lines array
Products = x.Lines.Select(y => y.ProductName)
})
.ToList();
var projectedResults = await asyncSession
.Query<Orders_ByCompanyAndShipToAndLines.IndexEntry, Orders_ByCompanyAndShipToAndLines>()
.Where(x => x.Company == "companies/65-A")
.Select(x => new
{
// Retrieve a property from an object
ShipToCity = x.ShipTo.City,
// Retrieve all product names from the Lines array
Products = x.Lines.Select(y => y.ProductName)
})
.ToListAsync();
public class Orders_ByCompanyAndShipToAndLines : AbstractIndexCreationTask<Order>
{
public class IndexEntry
{
public string Company { get; set; }
public Address ShipTo { get; set; }
public List<OrderLine> Lines { get; set; }
}
public Orders_ByCompanyAndShipToAndLines()
{
Map = orders => from order in orders
select new IndexEntry
{
Company = order.Company,
ShipTo = order.ShipTo,
Lines = order.Lines
};
}
}
// public class Address
// {
// public string Line1 { get; set; }
// public string Line2 { get; set; }
// public string City { get; set; }
// public string Region { get; set; }
// public string PostalCode { get; set; }
// public string Country { get; set; }
// public Location Location { get; set; }
// }
// public class OrderLine
// {
// public string Product { get; set; }
// public string ProductName { get; set; }
// public decimal PricePerUnit { get; set; }
// public int Quantity { get; set; }
// public decimal Discount { get; set; }
// }
// Using simple expression:
from index "Orders/ByCompanyAndShipToAndLines"
where Company == "companies/65-A"
select ShipTo.City as ShipToCity, Lines[].ProductName as Products
// Using JavaScript object literal syntax:
from index "Orders/ByCompanyAndShipToAndLines" as x
where Company == "companies/65-A"
select {
ShipToCity: x.ShipTo.City,
Products: x.Lines.map(y => y.ProductName)
}
Example IV - Projection with expression:
var projectedResults = session
.Query<Employees_ByNameAndTitle.IndexEntry, Employees_ByNameAndTitle>()
.Select(x => new
{
// Any expression can be provided for the projected content
FullName = x.FirstName + " " + x.LastName
})
.ToList();
var projectedResults = await asyncSession
.Query<Employees_ByNameAndTitle.IndexEntry, Employees_ByNameAndTitle>()
.Select(x => new
{
// Any expression can be provided for the projected content
FullName = x.FirstName + " " + x.LastName
})
.ToListAsync();
public class Employees_ByNameAndTitle : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
}
public Employees_ByNameAndTitle()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Title = employee.Title
};
}
}
from index "Employees/ByNameAndTitle" as x
select
{
FullName : x.FirstName + " " + x.LastName
}
Example V - Projection with calculations:
var projectedResults = session
.Query<Orders_ByCompanyAndShipToAndLines.IndexEntry, Orders_ByCompanyAndShipToAndLines>()
.Select(x => new
{
// Any calculations can be done within a projection
TotalProducts = x.Lines.Count,
TotalDiscountedProducts = x.Lines.Count(x => x.Discount > 0),
TotalPrice = x.Lines.Sum(l => l.PricePerUnit * l.Quantity)
})
.ToList();
var projectedResults = await asyncSession
.Query<Orders_ByCompanyAndShipToAndLines.IndexEntry, Orders_ByCompanyAndShipToAndLines>()
.Select(x => new
{
// Any calculations can be done within a projection
TotalProducts = x.Lines.Count,
TotalDiscountedProducts = x.Lines.Count(x => x.Discount > 0),
TotalPrice = x.Lines.Sum(l => l.PricePerUnit * l.Quantity)
})
.ToListAsync();
public class Orders_ByCompanyAndShipToAndLines : AbstractIndexCreationTask<Order>
{
public class IndexEntry
{
public string Company { get; set; }
public Address ShipTo { get; set; }
public List<OrderLine> Lines { get; set; }
}
public Orders_ByCompanyAndShipToAndLines()
{
Map = orders => from order in orders
select new IndexEntry
{
Company = order.Company,
ShipTo = order.ShipTo,
Lines = order.Lines
};
}
}
// public class Address
// {
// public string Line1 { get; set; }
// public string Line2 { get; set; }
// public string City { get; set; }
// public string Region { get; set; }
// public string PostalCode { get; set; }
// public string Country { get; set; }
// public Location Location { get; set; }
// }
// public class OrderLine
// {
// public string Product { get; set; }
// public string ProductName { get; set; }
// public decimal PricePerUnit { get; set; }
// public int Quantity { get; set; }
// public decimal Discount { get; set; }
// }
from index "Orders/ByCompanyAndShipToAndLines" as x
select {
TotalProducts: x.Lines.length,
TotalDiscountedProducts: x.Lines.filter(x => x.Discount > 0).length,
TotalPrice: x.Lines
.map(l => l.PricePerUnit * l.Quantity)
.reduce((a, b) => a + b, 0)
}
Example VI - Projecting using functions:
var projectedResults =
// Use LINQ query syntax notation
(from x in session
.Query<Employees_ByNameAndTitle.IndexEntry, Employees_ByNameAndTitle>()
// Define a function
let format =
(Func<Employees_ByNameAndTitle.IndexEntry, string>)(p =>
p.FirstName + " " + p.LastName)
select new
{
// Call the function from the projection
FullName = format(x)
})
.ToList();
var projectedResults =
// Use LINQ query syntax notation
await (from x in asyncSession
.Query<Employees_ByNameAndTitle.IndexEntry, Employees_ByNameAndTitle>()
// Define a function
let format =
(Func<Employees_ByNameAndTitle.IndexEntry, string>)(p =>
p.FirstName + " " + p.LastName)
select new
{
// Call the function from the projection
FullName = format(x)
})
.ToListAsync();
public class Employees_ByNameAndTitle : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
}
public Employees_ByNameAndTitle()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Title = employee.Title
};
}
}
declare function output(x) {
var format = p => p.FirstName + " " + p.LastName;
return { FullName: format(x) };
}
from index "Employees/ByNameAndTitle" as e select output(e)
Example VII - Projecting using a loaded document:
var projectedResults =
// Use LINQ query syntax notation
(from o in session
.Query<Orders_ByCompanyAndShippedAt.IndexEntry, Orders_ByCompanyAndShippedAt>()
// Use RavenQuery.Load to load the related Company document
let c = RavenQuery.Load<Company>(o.Company)
select new
{
CompanyName = c.Name, // info from the related Company document
ShippedAt = o.ShippedAt // info from the Order document
})
.ToList();
// Use LINQ query syntax notation
var projectedResults =
await (from o in asyncSession
.Query<Orders_ByCompanyAndShippedAt.IndexEntry, Orders_ByCompanyAndShippedAt>()
// Use RavenQuery.Load to load the related Company document
let c = RavenQuery.Load<Company>(o.Company)
select new
{
CompanyName = c.Name, // info from the related Company document
ShippedAt = o.ShippedAt // info from the Order document
})
.ToListAsync();
public class Orders_ByCompanyAndShippedAt : AbstractIndexCreationTask<Order>
{
public class IndexEntry
{
public string Company { get; set; }
public DateTime? ShippedAt { get; set; }
}
public Orders_ByCompanyAndShippedAt()
{
Map = orders => from order in orders
select new IndexEntry
{
Company = order.Company,
ShippedAt = order.ShippedAt
};
}
}
from index "Orders/ByCompanyAndShippedAt" as o
load o.Company as c
select {
CompanyName: c.Name,
ShippedAt: o.ShippedAt
}
Example VIII - Projection with dates:
var projectedResults = session
.Query<Employees_ByFirstNameAndBirthday.IndexEntry, Employees_ByFirstNameAndBirthday>()
.Select(x => new
{
DayOfBirth = x.Birthday.Day,
MonthOfBirth = x.Birthday.Month,
Age = DateTime.Today.Year - x.Birthday.Year
})
.ToList();
var projectedResults = await asyncSession
.Query<Employees_ByFirstNameAndBirthday.IndexEntry, Employees_ByFirstNameAndBirthday>()
.Select(x => new
{
DayOfBirth = x.Birthday.Day,
MonthOfBirth = x.Birthday.Month,
Age = DateTime.Today.Year - x.Birthday.Year
})
.ToListAsync();
public class Employees_ByFirstNameAndBirthday : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public DateTime Birthday { get; set; }
}
public Employees_ByFirstNameAndBirthday()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
Birthday = employee.Birthday
};
}
}
from index "Employees/ByFirstNameAndBirthday" as x
select {
DayOfBirth: new Date(Date.parse(x.Birthday)).getDate(),
MonthOfBirth: new Date(Date.parse(x.Birthday)).getMonth() + 1,
Age: new Date().getFullYear() - new Date(Date.parse(x.Birthday)).getFullYear()
}
Example IX - Projection with raw JavaScript code:
var projectedResults = session
.Query<Employees_ByFirstNameAndBirthday.IndexEntry, Employees_ByFirstNameAndBirthday>()
.Select(x => new
{
// Provide a JavaScript expression to the RavenQuery.Raw method
Date = RavenQuery.Raw<DateTime>("new Date(Date.parse(x.Birthday))"),
Name = RavenQuery.Raw(x.FirstName, "substr(0,3)")
})
.ToList();
var projectedResults = await asyncSession
.Query<Employees_ByFirstNameAndBirthday.IndexEntry, Employees_ByFirstNameAndBirthday>()
.Select(x => new
{
// Provide a JavaScript expression to the RavenQuery.Raw method
Date = RavenQuery.Raw<DateTime>("new Date(Date.parse(x.Birthday))"),
Name = RavenQuery.Raw(x.FirstName, "substr(0,3)")
})
.ToListAsync();
public class Employees_ByFirstNameAndBirthday : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public DateTime Birthday { get; set; }
}
public Employees_ByFirstNameAndBirthday()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
Birthday = employee.Birthday
};
}
}
from index "Employees/ByFirstNameAndBirthday" as x
select {
Date: new Date(Date.parse(x.Birthday)),
Name: x.FirstName.substr(0,3)
}
Example X - Projection with metadata:
var projectedResults = session
.Query<Employees_ByFirstNameAndBirthday.IndexEntry, Employees_ByFirstNameAndBirthday>()
.Select(x => new
{
Name = x.FirstName,
Metadata = RavenQuery.Metadata(x) // Get the metadata
})
.ToList();
var projectedResults = await asyncSession
.Query<Employees_ByFirstNameAndBirthday.IndexEntry, Employees_ByFirstNameAndBirthday>()
.Select(x => new
{
Name = x.FirstName,
Metadata = RavenQuery.Metadata(x) // Get the metadata
})
.ToListAsync();
public class Employees_ByFirstNameAndBirthday : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public DateTime Birthday { get; set; }
}
public Employees_ByFirstNameAndBirthday()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
Birthday = employee.Birthday
};
}
}
from index "Employees/ByFirstNameAndBirthday" as x
select {
Name : x.FirstName,
Metadata : getMetadata(x)
}
ProjectInto
-
Instead of
Select
, you can useProjectInto
to project all public fields from a generic type. -
The results will be projected into objects of the specified projection class.
var projectedResults = session
.Query<Companies_ByContactDetailsAndPhone.IndexEntry, Companies_ByContactDetailsAndPhone>()
.Where(x => x.ContactTitle == "owner")
// Call 'ProjectInto' instead of using 'Select'
// Pass the projection class
.ProjectInto<ContactDetails>()
.ToList();
// Each resulting object in the list is Not a 'Company' entity,
// it is an object of type 'ContactDetails'.
var projectedResults = await asyncSession
.Query<Companies_ByContactDetailsAndPhone.IndexEntry, Companies_ByContactDetailsAndPhone>()
.Where(x => x.ContactTitle == "owner")
// Call 'ProjectInto' instead of using 'Select'
// Pass the projection class
.ProjectInto<ContactDetails>()
.ToListAsync();
// Each resulting object in the list is Not a 'Company' entity,
// it is an object of type 'ContactDetails'.
public class Companies_ByContactDetailsAndPhone : AbstractIndexCreationTask<Company>
{
public class IndexEntry
{
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string Phone { get; set; }
}
public Companies_ByContactDetailsAndPhone()
{
Map = companies => companies
.Select(x => new IndexEntry
{
ContactName = x.Contact.Name,
ContactTitle = x.Contact.Title,
Phone = x.Phone
});
}
}
public class ContactDetails
{
// The projection class contains field names from the index-fields
public string ContactName { get; set; }
public string ContactTitle { get; set; }
}
from index "Companies/ByContactDetailsAndPhone"
where ContactTitle == "owner"
select ContactName, ContactTitle
SelectFields
The SelectFields
method can only be used by a Document Query.
It has two overloads:
// 1) Select fields to project by the projection class type
IDocumentQuery<TProjection> SelectFields<TProjection>();
// 2) Select specific fields to project
IDocumentQuery<TProjection> SelectFields<TProjection>(params string[] fields);
Using projection class type:
- The projection class fields are the fields that you want to project from the 'IndexEntry' class.
// Query an index with DocumentQuery
var projectedResults = session.Advanced
.DocumentQuery<Products_ByNamePriceQuantityAndUnits.IndexEntry,
Products_ByNamePriceQuantityAndUnits>()
// Call 'SelectFields'
// Pass the projection class type
.SelectFields<ProductDetails>()
.ToList();
// Each resulting object in the list is Not a 'Product' entity,
// it is an object of type 'ProductDetails'.
// Query an index with DocumentQuery
var projectedResults = await asyncSession.Advanced
.AsyncDocumentQuery<Products_ByNamePriceQuantityAndUnits.IndexEntry,
Products_ByNamePriceQuantityAndUnits>()
// Call 'SelectFields'
// Pass the projection class type
.SelectFields<ProductDetails>()
.ToListAsync();
// Each resulting object in the list is Not a 'Product' entity,
// it is an object of type 'ProductDetails'.
public class Products_ByNamePriceQuantityAndUnits : AbstractIndexCreationTask<Product>
{
public class IndexEntry
{
public string ProductName { get; set; }
public string QuantityPerUnit { get; set; }
public decimal PricePerUnit { get; set; }
public int UnitsInStock { get; set; }
public int UnitsOnOrder { get; set; }
}
public Products_ByNamePriceQuantityAndUnits()
{
Map = products => from product in products
select new IndexEntry
{
ProductName = product.Name,
QuantityPerUnit = product.QuantityPerUnit,
PricePerUnit = product.PricePerUnit,
UnitsInStock = product.UnitsInStock,
UnitsOnOrder = product.UnitsOnOrder
};
}
}
public class ProductDetails
{
// The projection class contains field names from the index-fields
public string ProductName { get; set; }
public decimal PricePerUnit { get; set; }
public int UnitsInStock { get; set; }
}
from index "Products/ByNamePriceQuantityAndUnits"
select ProductName, PricePerUnit, UnitsInStock
Using specific fields:
- The fields specified are the fields that you want to project from the projection class.
// Define an array with the field names that will be projected
var fields = new string[] {
"ProductName",
"PricePerUnit"
};
// Query an index with DocumentQuery
var projectedResults = session.Advanced
.DocumentQuery<Companies_ByContactDetailsAndPhone.IndexEntry,
Companies_ByContactDetailsAndPhone>()
// Call 'SelectFields'
// Pass the projection class type & the fields to be projected from it
.SelectFields<ProductDetails>(fields)
.ToList();
// Each resulting object in the list is Not a 'Product' entity,
// it is an object of type 'ProductDetails' containing data ONLY for the specified fields.
// Define an array with the field names that will be projected
var fields = new string[] {
"ProductName",
"PricePerUnit"
};
// Query an index with DocumentQuery
var projectedResults = await asyncSession.Advanced
.AsyncDocumentQuery<Companies_ByContactDetailsAndPhone.IndexEntry,
Companies_ByContactDetailsAndPhone>()
// Call 'SelectFields'
// Pass the projection class type & the fields to be projected from it
.SelectFields<ProductDetails>(fields)
.ToListAsync();
// Each resulting object in the list is Not a 'Product' entity,
// it is an object of type 'ProductDetails' containing data ONLY for the specified fields.
public class Products_ByNamePriceQuantityAndUnits : AbstractIndexCreationTask<Product>
{
public class IndexEntry
{
public string ProductName { get; set; }
public string QuantityPerUnit { get; set; }
public decimal PricePerUnit { get; set; }
public int UnitsInStock { get; set; }
public int UnitsOnOrder { get; set; }
}
public Products_ByNamePriceQuantityAndUnits()
{
Map = products => from product in products
select new IndexEntry
{
ProductName = product.Name,
QuantityPerUnit = product.QuantityPerUnit,
PricePerUnit = product.PricePerUnit,
UnitsInStock = product.UnitsInStock,
UnitsOnOrder = product.UnitsOnOrder
};
}
}
public class ProductDetails
{
// The projection class contains field names from the index-fields
public string ProductName { get; set; }
public decimal PricePerUnit { get; set; }
public int UnitsInStock { get; set; }
}
from index "Companies/ByContactDetailsAndPhone"
select ProductName, PricePerUnit
Projection behavior with a static-index
-
By default, when querying a static-index and projecting query results,
the server will try to retrieve the fields' values from the fields stored in the index.
If the index does Not store those fields then the fields' values will be retrieved from the documents. -
This behavior can be modified by setting the projection behavior.
-
Note: Storing fields in the index can increase query performance when projecting,
but this comes at the expense of the disk space used by the index.
Example
var projectedResults = session
.Query<Employees_ByNameAndTitleWithStoredFields.IndexEntry,
Employees_ByNameAndTitleWithStoredFields>()
// Call 'Customize'
// Pass the requested projection behavior to the 'Projection' method
.Customize(x => x.Projection(ProjectionBehavior.FromIndexOrThrow))
// Select the fields that will be returned by the projection
.Select(x => new EmployeeDetails
{
FirstName = x.FirstName,
Title = x.Title
})
.ToList();
var projectedResults = session.Advanced
.DocumentQuery<Employees_ByNameAndTitleWithStoredFields.IndexEntry,
Employees_ByNameAndTitleWithStoredFields>()
// Pass the requested projection behavior to the 'SelectFields' method
// and specify the field that will be returned by the projection
.SelectFields<EmployeeDetails>(ProjectionBehavior.FromIndexOrThrow)
.ToList();
var projectedResults = session.Advanced
// Define an RQL query that returns a projection
.RawQuery<EmployeeDetails>(
@"from index 'Employees/ByNameAndTitleWithStoredFields' select FirstName, Title")
// Pass the requested projection behavior to the 'Projection' method
.Projection(ProjectionBehavior.FromIndexOrThrow)
.ToList();
public class Employees_ByNameAndTitleWithStoredFields : AbstractIndexCreationTask<Employee>
{
public class IndexEntry
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
}
public Employees_ByNameAndTitleWithStoredFields()
{
Map = employees => from employee in employees
select new IndexEntry
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Title = employee.Title
};
// Store some fields in the index:
Stores.Add(x => x.FirstName, FieldStorage.Yes);
Stores.Add(x => x.LastName, FieldStorage.Yes);
}
}
public class EmployeeDetails
{
public string FirstName { get; set; }
public string Title { get; set; }
}
from index "Employees/ByNameAndTitleWithStoredFields"
select FirstName, Title
The projection behavior in the above example is set to FromIndexOrThrow
and so the following applies:
-
Field
FirstName
is stored in the index so the server will fetch its values from the index. -
However, field
Title
is Not stored in the index so an exception will be thrown when the query is executed.
Syntax
// For Query:
IDocumentQueryCustomization Projection(ProjectionBehavior projectionBehavior);
// For DocumentQuery:
IDocumentQuery<TProjection> SelectFields<TProjection>(
ProjectionBehavior projectionBehavior, params string[] fields);
IDocumentQuery<TProjection> SelectFields<TProjection>(
ProjectionBehavior projectionBehavior);
// Projection behavior options:
public enum ProjectionBehavior {
Default,
FromIndex,
FromIndexOrThrow,
FromDocument,
FromDocumentOrThrow
}
Default
Retrieve values from the stored index fields when available.
If fields are not stored then get values from the document.FromIndex
Retrieve values from the stored index fields when available.
A field that is not stored in the index is skipped.FromIndexOrThrow
Retrieve values from the stored index fields when available.
An exception is thrown if the index does not store the requested field.FromDocument
Retrieve values directly from the documents store.
A field that is not found in the document is skipped.FromDocumentOrThrow
Retrieve values directly from the documents store.
An exception is thrown if the document does not contain the requested field.
OfType (As)
-
Projection queries:
When querying an index with a projection (as described in this article), the server searches for documents that match the query predicate, but the returned objects follow the shape of the specified projection - they are not the actual documents. -
Non-Projection queries:
When querying an index without using a projection, the server returns the actual documents that match the query conditions. However, when filtering such a query by the index-entry fields, a type conversion is required for the compiler to understand the resulting objects' shape. This is where OfType (or As) comes into play as it. -
Role of
OfType
(orAs
):
So this is just a client-side type conversion used to map the query results to the document type.
It ensures the compiler recognizes that the resulting objects conform to the expected document shape. -
Results are tracked:
As opposed to projection queries where results are not tracked by the session,
In the case of non-projecting queries that use OfType, the session does track the resulting document entities.
List<Company> results = session
.Query<Companies_ByContactDetailsAndPhone.IndexEntry, Companies_ByContactDetailsAndPhone>()
// Here we filter by an IndexEntry field,
// The compiler recognizes 'x' as an IndexEntry type
.Where(x => x.ContactTitle == "owner")
// Here we let the compiler know that results are of type 'Company' documents
.OfType<Company>()
.ToList();
List<Company> results = await asyncSession
.Query<Companies_ByContactDetailsAndPhone.IndexEntry, Companies_ByContactDetailsAndPhone>()
// Here we filter by an IndexEntry field,
// The compiler recognizes 'x' as an IndexEntry type
.Where(x => x.ContactTitle == "owner")
// Here we let the compiler know that results are of type 'Company' documents
.OfType<Company>()
.ToListAsync();
public class Companies_ByContactDetailsAndPhone : AbstractIndexCreationTask<Company>
{
public class IndexEntry
{
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string Phone { get; set; }
}
public Companies_ByContactDetailsAndPhone()
{
Map = companies => companies
.Select(x => new IndexEntry
{
ContactName = x.Contact.Name,
ContactTitle = x.Contact.Title,
Phone = x.Phone
});
}
}
from index "Companies/ByContactDetailsAndPhone"
where ContactTitle == "owner"