Module:Cargo query/doc
This is the documentation page for Module:Cargo query
This module is the single entry point for all Cargo queries on HopperWiki. It wraps mw.ext.cargo.query() with schema-aware WHERE clause construction, automatic type detection, result parsing, deduplication, and table rendering.
The primary function is p.query (aliased as p.q). p.resource_query is a human-friendly wrapper over it for the Resource table. The older functions — filter_cargo_table_enhanced, filter_cargo_table, cargo_query, and thin_cargo_wrapper — have been removed; every template that called them has been migrated to p.query.
Architecture
The query pipeline runs in a fixed order regardless of which tier is used.
| Stage 1 · Arguments | Stage 2 · WHERE clause | Stage 3 · Query + parse | Stage 4 · Render |
|---|---|---|---|
|
Resolve args from frame and parent frame. Apply alias fallbacks. Build label map with automatic underscore→space defaults. |
Detect tier from args present. Build WHERE clause via schema-aware escaping. |
Run |
Evaluate collapse threshold. Render via |
Tiers
The WHERE clause tier is detected automatically from which arguments are present. You never declare which tier you are using — the module infers it.
| Tier | Detected when | Use case |
|---|---|---|
| 1 · Single field | filter_field is present and filters and where are absent |
One focal field, one or more include values, optional excludes against the same field |
| 2 · Multi-field structured | filters is present and where is absent |
Multiple fields composed with AND; values within a field composed with OR; schema-aware escaping applied automatically |
| 3 · Raw WHERE | where is present |
Full escape hatch — passed directly to Cargo. Use when Tier 2 cannot express the logic (date comparisons, LIKE, IS NULL) or when a value must be a template parameter substituted before #invoke runs
|
Tier detection priority: where wins over filters wins over filter_field. If none are present the query returns all rows — the caller's responsibility.
Parameters
Required
| Parameter | Notes |
|---|---|
table |
Cargo table name. Alias: cargo_table
|
fields |
Comma-separated fields to fetch from Cargo. Aliases: cargo_fields, query_fields
|
Display
| Parameter | Default | Notes |
|---|---|---|
display_fields |
Same as fields |
Fields to show as columns. Fetch a field in fields but omit it here to filter on it without displaying it. Aliases: cargo_display_fields, table_columns
|
column_labels |
Underscore→space for every field | Override labels for specific columns only. Format: Field_name=My label, Other_field=Other label. Fields not listed get automatic labels — you do not need to list every field.
|
format |
table |
table, bulleted_list, or count. count short-circuits before any rendering and returns a plain integer string.
|
row_cutoff |
3 |
Rows visible before the table collapses. The CSS class row-cutoff-N is applied at N+1 internally.
|
character_cutoff |
600 |
Maximum characters per cell before truncation with ellipsis. Not applied to file or list_of_file fields. |
default_file_fields |
— | Fallback image for empty file fields. Format: Field: File.jpg; OtherField: Default.png. Semicolon-separated.
|
options |
— | JSON string passed to transform_link_fields(). Supports link_text and link_text_fields keys.
|
Tier 1 — single field
| Parameter | Default | Notes |
|---|---|---|
filter_field |
— | The field to filter on. Must be the bare field name as declared in the Cargo schema — not Table.Field. Alias: cargo_focal_field
|
filter_values |
Current page title | Comma-separated values to include. Alias: filter_value
|
exclude_values |
— | Comma-separated values to exclude from the same field. Aliases: not_values, not_value
|
Tier 2 — multi-field structured
| Parameter | Notes |
|---|---|
filters |
Semicolon-separated filter clauses. Each clause is one of: Field HOLDS value1, value2 · Field = value · Field != value. Clauses are joined with AND. Multiple values within a HOLDS clause are joined with OR. Schema-aware escaping is applied automatically.
|
excludes |
Semicolon-separated exclusion clauses in the same syntax. Each becomes an AND NOT fragment appended after the main filters. |
Tier 3 — raw WHERE
| Parameter | Notes |
|---|---|
where |
Passed directly to cargo.query() as the WHERE clause. No escaping applied. Template parameter substitution (e.g. Identification) happens before #invoke runs, making this the correct tier when a value must be caller-configurable.
|
Query modifiers (all tiers)
| Parameter | Notes |
|---|---|
order_by |
ORDER BY clause. Example: Year_published DESC. Note: Year_published is a string field — ordering is lexicographic. Store years as zero-padded four-digit strings for correct sort behavior.
|
group_by |
GROUP BY clause. |
limit |
Hard cap on rows returned from Cargo before any deduplication. |
Clause syntax reference
Tier 2 clause fragments and how they compile:
| Input clause | Field type | Compiled WHERE fragment |
|---|---|---|
Species_purview HOLDS Melanoplus |
list_of_string | Species_purview HOLDS 'Melanoplus'
|
Species_purview HOLDS Melanoplus, Chorthippus |
list_of_string | (Species_purview HOLDS 'Melanoplus' OR Species_purview HOLDS 'Chorthippus')
|
Language = French |
list_of_string | Language HOLDS 'French' (auto-upgraded to HOLDS because field is a list type)
|
Language = French |
string | Language = 'French'
|
Language != French |
list_of_string | NOT (Language HOLDS 'French')
|
Language != French |
string | Language != 'French'
|
Year_published > 2000 |
any | Passed through unchanged (no recognized operator — treated as raw fragment) |
Multiple clauses in filters are joined: (...) AND (...) AND (...)
Multiple clauses in excludes each become: AND NOT (...)
Examples
Tier 1: single field, page title as filter
{{#invoke:Cargo_query|query
| table = Resource
| fields = Name, Year_published, Resource_link, Author
| filter_field = All_geography
| row_cutoff = 3
}}Fetches resources where All_geography HOLDS Cargo query/doc. Column labels default to Name, Year published, Resource link, Author — no column_labels needed.
Tier 1: with explicit filter value and excludes
{{#invoke:Cargo_query|query
| table = Resource
| fields = Name, Year_published, Resource_link, Language
| filter_field = Species_purview
| filter_values = Melanoplus devastator
| exclude_values = Unknown
| row_cutoff = 5
}}Tier 2: AND across two list fields
{{#invoke:Cargo_query|query
| table = Resource
| fields = Name, Long_title, Year_published, Resource_link, Author, Language, Descriptive_keyword
| display_fields = Name, Long_title, Year_published, Resource_link, Author, Language
| filters = Species_purview HOLDS {{PAGENAME}}; Descriptive_keyword HOLDS Identification
| excludes = Language = Chinese; Language = Japanese
| order_by = Year_published DESC
| row_cutoff = 5
}}Descriptive_keyword is fetched but excluded from display_fields — it drives the filter without appearing as a column.
Tier 2: geographic region + category
{{#invoke:Cargo_query|query
| table = Resource
| fields = Name, Long_title, Year_published, Resource_link, Author, Category
| filters = All_geography HOLDS {{PAGENAME}}; Category = Media
| order_by = Year_published DESC
| limit = 10
| row_cutoff = 5
}}Tier 3: raw WHERE with template parameter
{{#invoke:Cargo_query|query
| table = Resource
| fields = Name, Year_published, Resource_link
| where = Species_purview HOLDS '{{PAGENAME}}' AND Descriptive_keyword HOLDS '{{{keyword|Identification}}}'
}}Use Tier 3 when a value must be a caller-configurable template parameter. The Identification substitution happens before #invoke runs, which Tier 2 cannot accommodate.
Count format for conditional section display
{{#ifexpr: {{#invoke:Cargo_query|query
| table = Resource
| fields = Name
| filter_field = Species_purview
| format = count
}} > 0 | {{Species resources}} }}Post-query pipeline
After cargo.query() returns, results pass through these steps in order:
- Parse —
parse_cargo_results()splits list fields into Lua arrays using the delimiter declared in the Cargo schema (~~for most HopperWiki list fields). Scalar fields remain strings. - File fallbacks — Empty file and list_of_file fields are replaced with the value from
default_file_fieldsif provided. - Deduplication —
deduplicate_rows()removes duplicate rows across the display fields. - Link transformation —
transform_link_fields()rewrites url and list_of_url fields as labeled external links. - Collapse — If row count ≥
row_cutoff + 1, the CSS classcollapsible-cargo-table row-cutoff-Nis applied.
Fetch-but-don't-display pattern
Any field listed in fields but absent from display_fields is available to the WHERE clause but does not appear as a column. This is the standard approach for filter-only fields:
| fields = Name, Year_published, Descriptive_keyword
| display_fields = Name, Year_published
| filters = Descriptive_keyword HOLDS IdentificationDescriptive_keyword drives the filter. It never appears in the rendered table.
Known gotchas
| Situation | What happens | Fix |
|---|---|---|
filter_field = Table.Field |
Schema lookup fails — field not found | Use bare field name only: filter_field = Species_purview
|
| Comma in a filter value (Tier 2 HOLDS) | Value is split at the comma, producing two filter tokens | Move to Tier 3 and write the WHERE clause manually |
Year_published DESC with non-numeric values |
Lexicographic sort — "circa 1987" or "unknown" sort unpredictably | Store years as plain four-digit strings |
limit applied before deduplication |
Cargo caps rows before the pipeline runs — deduplication may reduce the final count below limit |
Set limit generously if deduplication is expected to remove rows
|
column_labels listing every field with Name=Name style entries |
Unnecessary — automatic underscore→space handles this | Omit column_labels entirely, or list only fields that need a genuinely custom label
|
| Tier 3 with unescaped user input | SQL injection risk | Never pass raw user-supplied values into where=. Use Tier 1 or Tier 2 for user-supplied values.
|
Removed functions
filter_cargo_table_enhanced, filter_cargo_table, cargo_query (and its aliases p.cargo / p.cargo_enhanced), and thin_cargo_wrapper have been removed from this module. All templates that called them were migrated to p.query:
| Old function | Replacement |
|---|---|
filter_cargo_table_enhanced |
p.query with filter_field
|
cargo_query / p.cargo / p.cargo_enhanced |
p.query with filters or where
|
thin_cargo_wrapper |
p.query with filters or where
|
filter_cargo_table |
Was dead code (scope errors, never callable) — deleted outright |
Module family
Module:Cargo_query is one of several modules in the HopperWiki utilities family. It depends on:
| Module | Role |
|---|---|
Module:Cargo query utilities |
Schema parsing, WHERE clause construction, row fetching |
Module:Cargo format utilities |
Result parsing, list splitting, URL and file formatting |
Module:Wiki output utilities |
Table and list rendering (generate_wiki_table_enhanced, bulleted_list)
|
Module:Utilities |
Re-exports all of the above for backward compatibility |
Module:Arguments |
Frame argument processing |