Skip to main content

tickets_custom_field_options

Selectable options catalog for choice-type custom fields (singleSelectMultipleChoice, multiSelectMultipleChoice). One row per (ticket, field, option) — the option catalog is captured per ticket (denormalized snapshot at the time of source sync), so the same option list is repeated for every ticket that carries the field.

Backfill: This table is auto-created on older datasets via migrate_tickets_custom_fields_tables in backend/services/sql.py.

Source Mapping

CouchDB document type: a "ticket" document — rows are produced by asyncconvert.py's tickets_custom_fields(obj) handler when a custom field is a choice type, by iterating customFields[i].richOptions[].

ColumnCouchDB sourceNotes
ticketididThe tickets.id this row belongs to.
field_idcustomFields[i].idThe custom field's identifier within the project.
option_idcustomFields[i].richOptions[j].idThe option's identifier within the field.
option_typecustomFields[i].richOptions[j].typeOption type marker from source (e.g. text, image).
option_textcustomFields[i].richOptions[j].textDisplay text for the option.
option_imagecustomFields[i].richOptions[j].imageOptional image reference for the option.
projectiddatabaseThe project this ticket belongs to.
couchdbidcouchDbIdLegacy CouchDB document identifier (the ticket's).

Only choice-type fields produce rows here. freetext, numeric, and date fields generate no options.

Column Reference

ColumnTypeNullableDescription
ticketidVARCHAR(255)noForeign-key reference to tickets.id. Part of the primary key.
field_idVARCHAR(255)noForeign-key reference to tickets_custom_fields.field_id (within the same ticket). Part of the primary key.
option_idVARCHAR(255)noThe option's identifier within the field. Part of the primary key.
option_typeVARCHAR(255)yesOption type marker from source (e.g. text, image).
option_textTEXTyesDisplay text for the option.
option_imageVARCHAR(255)yesOptional image reference for the option.
projectidVARCHAR(255)yesThe project this ticket belongs to.
couchdbidVARCHAR(255)yesLegacy CouchDB identifier of the ticket document.

Keys

  • Primary key: (ticketid, field_id, option_id)

Indexes

  • idx_ticket on ticketid
  • idx_field on field_id
  • idx_projectid on projectid

Relationships

DirectionOther tableJoinCardinality
outgoingtickets_custom_fields(ticketid, field_id)many options → one custom field
outgoingticketstickets_custom_field_options.ticketid = tickets.idmany rows → one ticket
outgoingprojectstickets_custom_field_options.projectid = projects.idmany rows → one project
incomingtickets_custom_field_selections(ticketid, field_id, option_id) = (ticketid, field_id, selected_option_id)one option → zero or one selection per ticket

None of these are foreign-keyed in MariaDB; joins are by string match.

Refresh Semantics

Rebuilt per ticket document alongside tickets_custom_fields and tickets_custom_field_selections. On each observation of a ticket, the converter:

  1. Issues DELETE FROM tickets_custom_field_options WHERE ticketid = <ticket_id> (when delete tracking is enabled).
  2. For each choice-type field on the ticket, emits one INSERT per element of customFields[i].richOptions[].

A reindex with type=ticket (or a full reindex) rebuilds this table.

Common Queries

Option catalog for a field on a ticket

SELECT option_id, option_text, option_type
FROM tickets_custom_field_options
WHERE ticketid = 'your_ticket_id'
AND field_id = 'your_field_id'
ORDER BY option_text;

Resolve the chosen options to their text

SELECT
cs.ticketid,
cs.field_id,
GROUP_CONCAT(co.option_text SEPARATOR ', ') AS selected_text
FROM tickets_custom_field_selections cs
JOIN tickets_custom_field_options co
ON co.ticketid = cs.ticketid
AND co.field_id = cs.field_id
AND co.option_id = cs.selected_option_id
WHERE cs.ticketid = 'your_ticket_id'
GROUP BY cs.ticketid, cs.field_id;

Most-used options across a project

SELECT
co.option_text,
COUNT(DISTINCT cs.ticketid) AS tickets_selecting
FROM tickets_custom_field_options co
JOIN tickets_custom_field_selections cs
ON cs.ticketid = co.ticketid
AND cs.field_id = co.field_id
AND cs.selected_option_id = co.option_id
WHERE co.projectid = 'your_project_id'
GROUP BY co.option_text
ORDER BY tickets_selecting DESC;

Pitfalls

  • Backfill table. Auto-created on older datasets via migrate_tickets_custom_fields_tables.
  • Per-ticket snapshot of the option catalog. Each ticket carries its own copy of the option list as it stood when the ticket was synced. If a project edits an option's text, existing tickets keep the old text until they are re-synced. Aggregations across the whole project should be aware of this.
  • Composite identity. Joins to selections must use all three columns (ticketid, field_id, option_id) — option_id alone is not unique because the catalog repeats per ticket.
  • Choice fields only. freetext, numeric, and date fields produce no rows here. Their value lives on tickets_custom_fields.field_value directly.
  • Reading during an in-flight reindex of a ticket may briefly return zero rows for that ticket.
  • No FK constraints. Joins are by string match.