Couchbase allows you store heterogeneous documents within a Bucket or "keyspace". When you do a select without a predicate, you are querying across the entire keyspace. For example, imagine a bucket with various document "types" and you do a N1QL query like this:
SELECT name FROM `travel-sample`;
This query will return the name field for every document in the keystore. It's more likely you would want to constrain the query to a subset of the entire keystore, returning results from only one kind of document, so you would do something like this:
SELECT name FROM `travel-sample` WHERE type="airline";
In your application, this would translate to a Linq query that maps the document to an C# object with strongly typed fields matching the document:
var query = from x in db.Query<Airline>()
where x.Type == "airline"
select x.Name;
Now, this is nice and all but becomes a but tedious to do for every query. Instead what you want to do is remove the explicit Type predicate from the query:
var query = from x in db.Query<Airline>()
select x.Name;
This is exactly what Document Filters allow you to do: map a JSON document to a C# POCO without explicitly (and redundantly) having to provide a predicate (WHERE) based on the "type" field. Instead you create either create a custom DocumentFilter or you use a DocumentTypeFilterAttribute and you associate it with your C# class or POCO.
Assuming we have a document that looks like this:
{
"id": 24,
"type": "airline",
"name": "American Airlines",
"iata": "AA",
"icao": "AAL",
"callsign": "AMERICAN",
"country": "United States"
}
We can have an associated Airline class that is symmetrical in form and shape:
[DocumentTypeFilter("airline")]
public class Airline
{
[JsonIgnore]
public string Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("iata")]
public string Iata { get; set; }
[JsonProperty("icao")]
public string Icao { get; set; }
[JsonProperty("callsign")]
public string Callsign { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
}
Now when we generate a Linq query, the WHERE type="airline" will be automatically added to the emitted N1QL query and only document of type "airline" will be returned.
In addition to the default filters, custom filters are also supported which enable you to apply any predicate you wish to a POCO. To use a custom document filter, you create an implementation of IDocumentFilter which matches the criterion you wish to filter by. Here is an example of a custom filter which filters by Type:
class BreweryFilter : IDocumentFilter<Brewery>
{
public int Priority { get; set; }
public IQueryable<Brewery> ApplyFilter(IQueryable<Brewery> source)
{
return source.Where(p => p.Type == "brewery");
}
}
Note that the predicate can be anything you wish even compound.
You have two options for applying a custom document filter. The first is to make a custom filter attribute inherited from DocumentFilterAttribute. Then apply this attribute to your document types.
The second is to register the filter using the DocumentFilterManager for each document type:
DocumentFilterManager.SetFilter<DocumentType>(new BreweryFilter());
Typically this would done when the application starts up, or you could do it in the ctor of a class which extends BucketContext.
For an example of a dynamic document filter attribute inherited from DocumentFilterAttribute, see the source code for DocumentTypeFilterAttribute.