# Facets configuration

The `facets` option is a required entry within the [facets table configuration](configuration.md#configuration). It lets you configure which source columns are used to read facet values and labels.

For each facet, you can:

- Specify the source columns from which to read option values and labels.
- Extract option values and labels from arrays - see [Array values](#array-values).
- Extract option values and labels from JSON objects - see [JSON data](#json-data).
- Group entries into distinct categories - see [Buckets](#buckets).

## facet_name

_Required_

The name identifier of the facet.

Assumes that a column with this name exists in the source table - values will be read from that column.
Otherwise, use config option [value_column](#value_column) to read data from a different source column
(and in that case the facet name may be an arbitrary, descriptive name).

## facet_label

In frontend, the facet label describes the set of facet options. In this example "System Status":

```text
System Status
-------------
[x] Approved
[ ] Draft
```

The facet label is usually a string, but it can also be extracted from a JSON object - useful when providing translations without having to implement an interface text lookup function.

### With a string value

Writes a static text to the `facet_label` column.

#### Examples

Source table

```text
| id           | state    | state_label    |
| [PK] integer | text     | text           |
| ------------ | -------- | -------------- |
| 1            | draft    | Draft          |
| 2            | approved | Approved       |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "state",
      label_column: "state_label",
      facet_label: "System Status"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label | facet_label     |
| ---------- | ------------ | ------------ | --------------- |
| status     | draft        | "Draft"      | "System Status" |
| status     | approved     | "Approved"   | "System Status" |
```

### With a JSON value

Wrties a static JSON object to the `facet_label` column.

#### Examples

Source table

```text
| id           | state    | state_label    |
| [PK] integer | text     | text           |
| ------------ | -------- | -------------- |
| 1            | draft    | Draft          |
| 2            | approved | Approved       |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "state",
      label_column: "state_label",
      facet_label: %{en: "Status", de: "Zustand"}
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label | facet_label                       |
| ---------- | ------------ | ------------ | --------------------------------- |
| status     | draft        | "Draft"      | {"en": "Status", "de": "Zustand"} |
| status     | approved     | "Approved"   | {"en": "Status", "de": "Zustand"} |
```

## value_column

By default, values are read from the source column corresponding to the facet name. Use `value_column` to specify a different source column.

### Examples

Source table

```text
| id           | state    |
| [PK] integer | text     |
| ------------ | -------- |
| 1            | draft    |
| 2            | approved |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "status",
      value_column: "state"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| status     | draft        | "draft"      |
| status     | approved     | "approved"   |
```

## label_column

By default, labels are read from the value column. Use `label_column` to specify a different source column.

### Examples

Source table

```text
| id           | state    | state_label    |
| [PK] integer | text     | text           |
| ------------ | -------- | -------------- |
| 1            | draft    | Draft          |
| 2            | approved | Approved       |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "state",
      label_column: "state_label"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| status     | draft        | "Draft"      |
| status     | approved     | "Approved"   |
```

## value_path

Extracts the value from a JSON object.

The path is a comma-separated string of keys and/or indexes. For example:

- `"value"`
- `"path,to,value"`
- `"path,to,value,1"`

The last example corresponds to an array value in a nested object:

```json
{
  "path": {
    "to": {
      "value": ["dummy", ["memory", "oral-history", "interdisciplinary"]]
    }
  }
}
```

For applications of `value_path`, see: [JSON data](#json-data)

### Examples

Source table

```text
| id           | word_count     |
| [PK] integer | jsonb          |
| ------------ | -------------- |
| 1            | {"value": 999} |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "word_count",
      value_path: "value"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| word_count | 999          | "999"        |
```

## label_path

Extracts the label from a JSON object.

Without option `label_path`, the label is created from the value at `value_path`.

- See also [value_path](#value_path)
- For applications of `label_path`, see: [JSON data](#json-data)

### Examples

Source table

```text
| id           | role                                   |
| [PK] integer | jsonb                                  |
| ------------ | -------------------------------------- |
| 1            | {"value": "editor", "label": "Editor"} |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "role",
      value_path: "value",
      label_path: "label"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| role       | editor       | "Editor"     |
```

## buckets

Buckets divide numeric and date entries into discrete categories. For example: movies released between 2010 and 2020, or prices ranging from EUR 20 to 50.

Use config option `buckets` with an array of **numeric**, **date** or **timestamp** values to define the bucket boundaries for the option values.
Facet option values are grouped into intervals defined by these boundaries, and labels are generated for each bucket.
The generated bucket label will overwrite any other label options.

> #### Sorted bucket values {: .info}
>
> Bucket values are automatically sorted by Refine, so input values do not need to be ordered.

### Numbers

Facet configuration setting:

```
buckets: [0, 1000, 2000]
```

Generates the following bucket numbers and labels (if resources match all bins):

```text
| Bucket number | Label         |
| ------------- | ------------- |
| 0             | "< 0"         |
| 1             | "0 - 1000"    |
| 2             | "1000 - 2000" |
| 3             | "> 2000"      |
```

### Dates

Facet configuration setting:

```
buckets: [~D[1950-01-01], ~D[1960-01-01], ~D[1970-01-01]]
```

Generates the following bucket numbers and labels (if resources match all bins):

```text
| Bucket number | Label                     |
| ------------- | ------------------------- |
| 0             | "< 1950-01-01"            |
| 1             | "1950-01-01 - 1960-01-01" |
| 2             | "1960-01-01 - 1970-01-01" |
| 3             | "> 1970-01-01"            |
```

### Timestamps and intervals

Facet configuration setting:

```
buckets: [
  "now()",
  "now() - INTERVAL '1 day'",
  "now() - INTERVAL '1 week'",
  "now() - INTERVAL '1 month'",
  "now() - INTERVAL '1 year'"
]
```

Generates the following bucket numbers and labels (if resources match all bins):

```text
| Bucket number | Label                                       |
| ------------- | ------------------------------------------- |
| 1             | "2025-01-01 00:00:00 - 2025-12-01 00:00:00" |
| 1             | "2025-12-01 00:00:00 - 2025-12-25 00:00:00" |
| 2             | "> 2026-01-01 00:00:00"                     |
```

The date-times will, of course, be different in reality.

Instead of using `now()`, you may also use:

- `current_date`
- Specific dates (as date strings). Dates need to be wrapped in single quotes and contain a `::date` type suffix.

Example with PostgreSQL variable `current_date`:

```
buckets: [
  "current_date",
  "current_date - INTERVAL '1 day'",
  "current_date - INTERVAL '1 week'",
  "current_date - INTERVAL '1 month'",
  "current_date - INTERVAL '1 year'"
]
```

Example with specific dates:

```
day = ~D[2026-01-01] |> to_string()
pg_date = "'#{day}'::date"

buckets: [
  "#{pg_date}",
  "#{pg_date} - INTERVAL '1 day'",
  "#{pg_date} - INTERVAL '1 week'",
  "#{pg_date} - INTERVAL '1 month'",
  "#{pg_date} - INTERVAL '1 year'"
]
```

### Examples

#### With option value_column

Fetches the numeric value to be bucketed from the source table column.

Source table

```text
| id           | word_count |
| [PK] integer | integer    |
| ------------ | ---------- |
| 1            | 1400       |
| 2            | 200        |
| 3            | 9000       |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "word_count",
      buckets: [0, 1000, 2000, 3000],
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label  |
| ---------- | ------------ | ------------- |
| word_count | 1            | "0 - 1000"    |
| word_count | 2            | "1000 - 2000" |
| word_count | 4            | "> 3000"      |
```

#### With option value_path

Fetches the numeric value to be bucketed from the specified key in the source table JSON column.

Source table

```text
| id           | word_count      |
| [PK] integer | jsonb           |
| ------------ | --------------- |
| 1            | {"value": 1400} |
| 2            | {"value": 200}  |
| 3            | {"value": 9000} |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "word_count",
      value_path: "value",
      buckets: [0, 1000, 2000, 3000],
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label  |
| ---------- | ------------ | ------------- |
| word_count | 1            | "0 - 1000"    |
| word_count | 2            | "1000 - 2000" |
| word_count | 4            | "> 3000"      |
```

## Array values

If the value column is an array, its elements are expanded into separate rows.

The following data source formats are supported:

- Value column contains arrays
- Both value and label columns contain arrays
- Value column contains arrays and label column contains JSON

### Value column contains arrays

**Examples**

Source table

```text
| id           | tags             |
| [PK] integer | text[]           |
| ------------ | ---------------- |
| 1            | {history,memory} |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "tags"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| tags       | history      | "history"    |
| tags       | memory       | "memory"     |
```

### Both value and label columns contain arrays

If both the value and label columns are arrays, they are treated as parallel arrays and expanded positionally using ordinality.

**Examples**

Source table

```text
| [PK] integer | text[]           | text[]           |
| id           | tags             | tag_labels       |
| ------------ | ---------------- | ---------------- |
| 1            | {history,memory} | {History,Memory} |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "tags",
      label_column: "tag_labels"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| tags       | history      | "History"    |
| tags       | memory       | "Memory"     |
```

### Value column contains arrays and label column contains JSON

The label column may contain JSON data, for example a lookup map for translations.

**Examples**

Source table

```text
| id           | tags            | tag_translations                                   |
| [PK] integer | text[]          | jsonb                                              |
| ------------ | --------------- | -------------------------------------------------- |
| 1            | {books,history} | {                                                  |
|              |                 |   "books": {"de": "Bücher", "en": "Books"},        |
|              |                 |   "history": {"de": "Geschichte", "en": "History"} |
|              |                 | }                                                  |
```

Facet configuration

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "tags",
      label_column: "tag_translations"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label                          |
| ---------- | ------------ | ------------------------------------- |
| tags       | books        | {"de": "Bücher", "en": "Books"}       |
| tags       | history      | {"de": "Geschichte", "en": "History"} |
```

## JSON data

Source data stored in JSON format can be extracted and used as facet option values and labels in several different ways.
Depending on the structure of the JSON data, facet option values and labels can be derived directly, paired explicitly,
or mapped from arrays and key-value objects.

The following data source formats are supported:

- Value entry
- Value and label entry
- Value array
- Parallel value and label arrays
- Label as key-value map

### Value entry

Use the option [value_path](#value_path) to extract the value from the JSON object.

### Value and label entry

When both option value and label are present in the JSON data, use config option [label_path](#label_path) to extract the label from the JSON.

### Value array

If value is a JSON array, its elements are expanded into separate rows.

**Examples**

Source table

```text
| id           | tags                             |
| [PK] integer | jsonb                            |
| ------------ | -------------------------------- |
| 1            | {"value": ["history", "memory"]} |
```

Facet configuration

Use facet option `value_path` to fetch the value from from the JSON column.

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "tags",
      value_path: "value"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| tags       | history      | "history"    |
| tags       | memory       | "memory"     |
```

### Parallel value and label arrays

If both value and label are JSON arrays, they are treated as parallel arrays. Each value is paired with the label at the same position.

**Examples**

Source table

```text
| id           | tags                              |
| [PK] integer | jsonb                             |
| ------------ | --------------------------------- |
| 1            | {                                 |
|              |   "value": ["history", "memory"], |
|              |   "label": ["History", "Memory"]  |
|              | }                                 |
```

Facet configuration

Use facet options `value_path` and `label_path` to extract the values and labels from both JSON arrays:

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "tags",
      value_path: "value",
      label_path: "label"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| tags       | history      | "History"    |
| tags       | memory       | "Memory"     |
```

### Label as key-value map

If value is a JSON array and label is a JSON map, each value is used as a key to look up its corresponding label.

**Examples**

Source table

```text
| id           | languages                                    |
| [PK] integer | jsonb                                        |
| ------------ | -------------------------------------------- |
| 1            | {"value": ["de", "en", "fr"],                |
|              |  "label": {"de": "German", "en": "English",  |
|              |            "fr": "French"}}                  |
```

Facet configuration

Use config options `value_path` and `label_path` to extract the values from the JSON array and the labels from the JSON object:

```elixir
config = %{
  ...
  facets: [
    %{
      facet_name: "languages",
      value_path: "value",
      label_path: "label"
    }
  ]
}
```

Facets table (simplified)

```text
| facet_name | option_value | option_label |
| ---------- | ------------ | ------------ |
| languages  | de           | "German"     |
| languages  | en           | "English"    |
| languages  | fr           | "French"     |
```
