Skip to main content

audits_category

One row per category copied from the audit's template into a specific audit instance. Categories are the top-level grouping of questions within an audit (e.g. "Roof", "Foundation", "Electrical").

Source Mapping

CouchDB document type: an "audit" document — rows are produced by asyncconvert.py's audits_category(obj) handler iterating obj.questions[*]. Each top-level entry in obj.questions is one category.

ColumnCouchDB sourceNotes
categorynameobj.questions[i].categoryNameHuman-readable category name. Free text from the template.
auditobj.idThe audits.id this category belongs to.
categoryidcomputed (hash)hash(categoryName). Deterministic from the name alone — see Pitfalls.
projectidobj.databaseThe project this audit belongs to.
couchdbidobj.couchDbIdLegacy CouchDB document identifier (the audit's).

Column Reference

ColumnTypeNullableDescription
categorynameTEXTyesHuman-readable category name (free text).
auditVARCHAR(255)yesForeign-key reference to audits.id.
categoryidVARCHAR(255)yesComputed identifier — hash(categoryName). Same name → same categoryid across audits.
projectidVARCHAR(255)yesThe project this audit belongs to.
couchdbidVARCHAR(255)yesLegacy CouchDB identifier of the audit document.

Keys

  • No primary key, no unique constraint. A category appears once per audit, but the same categoryid recurs across every audit that uses a category with the same name. Uniqueness within an audit is enforced procedurally by the converter (delete-then-reinsert per audit document).

Indexes

  • idx_categoryid on categoryid
  • idx_project on projectid
  • idx_parent on audit

Relationships

DirectionOther tableJoinCardinality
outgoingauditsaudits_category.audit = audits.idmany categories → one audit
outgoingprojectsaudits_category.projectid = projects.idmany rows → one project
incomingaudits_questionsaudits_questions.audit = audits_category.audit AND audits_questions.categoryid = audits_category.categoryidone category → many questions
incomingaudits_answeraudits_answer.audit = audits_category.audit AND audits_answer.categoryid = audits_category.categoryidone category → many answers

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

Refresh Semantics

audits_category is rebuilt per audit document. On each observation of an audit, the converter:

  1. Issues DELETE FROM audits_category WHERE audit = <audit_id> (when delete tracking is enabled).
  2. Iterates obj.questions[*] and inserts one row per category.

Reading during an in-flight reindex of an audit may briefly return zero rows for that audit. Reindex again if a partial reindex was interrupted.

A reindex with type=audit (or a full reindex) rebuilds this table as part of the audit reindex. There is no separate type=audits_category target.

Common Queries

Categories within an audit

SELECT categoryid, categoryname
FROM audits_category
WHERE audit = 'your_audit_id'
ORDER BY categoryname;

Most common category names across a project

SELECT categoryname,
COUNT(DISTINCT audit) AS audits_with_category
FROM audits_category
WHERE projectid = 'your_project_id'
GROUP BY categoryname
ORDER BY audits_with_category DESC;

Question counts per category

SELECT
ac.audit,
ac.categoryname,
COUNT(aq.questionid) AS question_count
FROM audits_category ac
LEFT JOIN audits_questions aq
ON aq.audit = ac.audit
AND aq.categoryid = ac.categoryid
WHERE ac.projectid = 'your_project_id'
GROUP BY ac.audit, ac.categoryname
ORDER BY ac.audit, question_count DESC;

Pitfalls

  • categoryid is deterministic from the name. Two audits with a category named "Roof" will share the same categoryid. Joining audits_category rows on categoryid alone collapses across audits. Always also filter or join on audit (or projectid) when you need per-audit identity.
  • No primary key. If a partial reindex fails between the DELETE and the INSERTs, an audit may briefly have zero categories. Reindex again to recover.
  • categoryname is free text from the template. Spelling and casing differences across templates create separate categoryid values for what users would consider "the same" category. Cross- template rollups by category have to do their own normalization.