# Advanced model options

# Complex indexing

When creating a model, the property descriptor can contain an index attribute to specify that particular property as an index. If you need to define a more complex index, such as on multiple properties, you can provide the index field in the options object instead.

Let's use this as our model's schema for all the following examples:

const schema = {
  brand: String,
  color: String,
  price: Number
}

options.index accepts a variety of index definitions:

# single property index

This is equivalent to specifying the index in the property descriptor, except the index name is automatically generated.

db.model('shoes', schema, { index: 'brand' })

# multiple property index

Create a single index on all the specified properties.

db.model('shoes', schema, { index: ['brand', 'color']})

# multiple indices on multiple properties

Create an index for each set of properties.

db.model('shoes', schema, {
  index: [
    ['brand', 'color'],
    ['color', 'price']
  ]
})

# named indices

All other forms will automatically generate a unique index name of the format index_property1[_property2[_property3...]]. If you want the index to have a specific custom name, use an object instead.

db.model('shoes', schema, {
  index: {
    idx_brand: 'brand',
    idx_brand_color: ['brand', 'color'],
    idx_color_price: ['color', 'price']
  }
})

# Primary and unique fields

When you use the primary or unique attributes on an individual property, the primary key or unique index consists only of that property.

db.model('cars', {
  make: { type: String, primary: true },
  model: String
})

However there are many cases where you want the primary key to consist of multiple properties. For example let's assume we now want make and model to be a composite primary key, meaning both fields are needed to uniquely identify an object in the model. To do this in trilogy, remove the primary or unique attributes from the individual properties and instead declare them in the ModelOptions:

  db.model('cars', {
-   make: { type: String, primary: true },
+   make: String,
    model: String
- })
+ }, {
+   primary: ['make', 'model']
+ })

Now, the combination of both fields is checked for uniqueness when objects are created in this model. The same applies to unique, but here these fields are each individually declared as unique constraints, not as a composite key.

db.model('cars', {
  make: String,
  model: String
}, {
  unique: ['make', 'model']
})

# Timestamps

Rather than defining your own properties for creation or updated dates, you can use the timestamps option to define these on your model. When timestamps is true, created_at and updated_at properties are defined, with both defaulting to the current time.

db.model('users', {
  name: { type: String, primary: true }
}, {
  timestamps: true
})

Note that for the updated_at timestamp to automatically update when you make changes, a primary key or unique constraint is required on your model so trilogy can uniquely identify the updated record(s). If the model above had been defined like this:

db.model('users', {
  name: String
}, {
  timestamps: true
})

... then there's no way for trilogy to uniquely identify updated records. In that case you can use trilogy's lifecycle hooks to keep the timestamp up to date yourself, which will be covered in the next section.