Duplicated Fields for Faster Querying
One option to speed up queries against the JSONB documents is by duplicating a property or field within the JSONB structure as a separate database column on the document table. When you issue a Linq query using this duplicated property or field, Marten is able to write the SQL query to run against the duplicated field instead of using JSONB operators. This of course only helps for queries using the duplicated field.
TIP
we strongly recommend using Computed Indexes/Calculated Indexes over duplicated fields for most cases to speed up queries. Calculated indexes optimize the querying of a document type without incurring potentially expensive schema changes and extra runtime insert costs.
Also note that there are few cases where calculated index does not work i.e DateTime
, DateTimeOffset
fields, resort to using duplicated fields for these.
To create a duplicated field, you can use the [DuplicateField]
attribute like this:
[PropertySearching(PropertySearching.ContainmentOperator)]
public class Employee
{
public int Id;
// You can optionally override the Postgresql
// type for the duplicated column in the document
// storage table
[DuplicateField(PgType = "text")]
public string Category;
// Defining a duplicate column with not null constraint
[DuplicateField(PgType = "text", NotNull = true)]
public string Department;
}
Or by using the fluent interface off of StoreOptions
:
var store = DocumentStore.For(options =>
{
// Add a gin index to the User document type
options.Schema.For<User>().GinIndexJsonData();
// Adds a basic btree index to the duplicated
// field for this property that also overrides
// the Postgresql database type for the column
options.Schema.For<User>().Duplicate(x => x.FirstName, pgType: "varchar(50)");
// Defining a duplicate column with not null constraint
options.Schema.For<User>().Duplicate(x => x.Department, pgType: "varchar(50)", notNull: true);
// Customize the index on the duplicated field
// for FirstName
options.Schema.For<User>().Duplicate(x => x.FirstName, configure: idx =>
{
idx.Name = "idx_special";
idx.Method = IndexMethod.hash;
});
// Customize the index on the duplicated field
// for UserName to be unique
options.Schema.For<User>().Duplicate(x => x.UserName, configure: idx =>
{
idx.IsUnique = true;
});
// Customize the index on the duplicated field
// for LastName to be in descending order
options.Schema.For<User>().Duplicate(x => x.LastName, configure: idx =>
{
idx.SortOrder = SortOrder.Desc;
});
});
In the case above, Marten would add an extra columns to the generated mt_doc_user
table with first_name
and department
. Some users find duplicated fields to be useful for user supplied SQL queries.
Defining Not Null constraint
By default, the duplicate column is created with NULL constraint. If you want to define the duplicate column with a NOT NULL constraint, use NotNull
property via DuplicateFieldAttribute
or pass notNull: true
for the Duplicate
fluent interface. See the examples above.
Indexing
By default, Marten adds a btree index (the Postgresql default) to a searchable index, but you can also customize the generated index with the syntax shown above: The second nested closure argument is an optional mechanism to customize the database index generated for the duplicated field.