Facets
A facet is one filter dimension. It defines what data source to read from, what UI widget to render, and how to translate a user selection into a WP_Query clause. Everything else in Grid Panda — the index, the grid counts, the REST API payload — revolves around facets.
Database Structure
Facets are stored in the wp_gridpanda_facets table. Each row is one facet definition:
| Column | Type | Description |
|---|---|---|
| id | bigint unsigned | Auto-increment primary key |
| name | varchar(255) | Human-readable label shown above the filter widget |
| slug | varchar(200) UNIQUE | URL-safe identifier used in fx_ query params and clean URLs |
| type | varchar(50) | Filter widget type: checkbox, radio, range, color, etc. |
| source | varchar(255) | Data source: taxonomy, post_meta, post_field, or wc |
| config | longtext (JSON) | Type-specific configuration as a JSON object |
| post_type | varchar(100) | WordPress post type this facet indexes against |
| display_order | int | Sort order in the facets admin list |
| created_at / updated_at | datetime | Record timestamps |
The slug column has a unique index and is used as the parameter name in filtered URLs: ?fx_{slug}=value or/filter/{slug}-value/ in clean URL mode.
Source Types
The source field tells Grid Panda where to read facet values from when indexing posts, and what kind of WP_Query clause to build when filtering. The format is source_type:source_key:
taxonomy:{taxonomy_slug}Reads from WordPress terms assigned to a post. At index time, stores term slugs as facet values and term names as display labels. At query time, generates a tax_query clause. Supports include_children for hierarchical taxonomies.
taxonomy:categorytaxonomy:post_tagtaxonomy:pa_colorQuery output: tax_query: [{ taxonomy, field: 'slug', terms, operator: 'IN' }]
post_meta:{meta_key}Reads from wp_postmeta. Supports serialized arrays — if a meta value is a PHP serialized array, each element is indexed as a separate row. ACF fields are supported; the display value uses the ACF field label where available.
post_meta:_pricepost_meta:brandpost_meta:acf_colorQuery output: meta_query: [{ key, value, compare: 'IN' or 'BETWEEN' }]
post_field:{field_name}Reads from standard WP_Post fields. Author is indexed as user ID (value) and display name (label). Date is indexed as YYYY-MM-DD. Generates native WP_Query args rather than meta_query/tax_query.
post_field:post_authorpost_field:post_datepost_field:post_statusQuery output: author__in, post_status, date_query, etc.
wc:{wc_field}WooCommerce-specific sources. stock_status reads _stock_status meta. on_sale uses wc_get_product_ids_on_sale(). Attribute sources index WC product attribute taxonomies. Requires WooCommerce to be active.
wc:stock_statuswc:on_salewc:attribute:pa_sizeQuery output: meta_query on _stock_status / post__in from on_sale / tax_query on pa_* attribute
Available Facet Types
The type field determines the filter UI widget and query logic. Grid Panda ships 14 built-in types:
checkboxMulti-select checkboxes with optional hierarchy, count display, and show-more truncation. Supports OR/AND/NOT operators.
radioSingle-select radio buttons with an optional 'All' option that clears the filter. Single value only.
dropdownNative <select> element with single or multi-select mode, optional placeholder, and count display.
button_groupInline pill buttons with configurable size (sm/md/lg), wrap or horizontal scroll layout, single or multi-select.
rangeDual-handle slider for numeric ranges with configurable prefix/suffix, decimal precision, and step size.
date_rangeDate picker inputs or preset buttons (last 7 days, this month, etc.) for filtering by post date or date meta fields.
searchText input for full-text search or field-scoped search (title only, meta LIKE, taxonomy term search).
autocompleteAJAX typeahead that fetches matching values from the index as the user types. Supports single and multi-select.
ratingStar rating filter (1–5 or custom max). Supports 'and up' (gte) or exact match mode. Aggregates tier counts from index.
colorColor swatch grid/list with configurable size, shape (circle/square/rounded), and hex color or gradient mapping.
toggleBinary on/off switch that filters by a specific field value (e.g. is_featured=1, stock_status=instock).
hierarchyCollapsible tree for hierarchical taxonomies. Shows parent → child relationships from the index depth/parent_id columns.
sortSort order control rendered as dropdown, radio, or button group. Modifies WP_Query orderby/order rather than filtering.
active_filtersDisplays currently-active selections as removable pills. Purely a UI widget — contributes nothing to the query.
The config JSON Field
Each facet row has a config column storing a JSON object with all type-specific settings. The exact fields depend on the facet type. Here is a checkbox facet example:
{
"show_count": true,
"show_more_count": 5,
"show_search": false,
"show_clear_button": true,
"hide_empty": false,
"show_empty_as_disabled": false,
"operator": "OR",
"max_selections": 0,
"order_by": "count",
"order": "desc",
"min_count": 0,
"hierarchical": false,
"show_more_label": "Show More",
"show_less_label": "Show Less",
"search_placeholder": "Search…"
}Config is validated server-side via each type's validate_config() method before saving. Invalid values are either rejected with a 422 error or silently replaced with safe defaults.
How Facets Are Indexed
When a facet is saved, or when a reindex is triggered, Grid Panda runs all published posts of the matchingpost_type through each facet's data resolver. The resolver extracts the raw values and writes rows to wp_gridpanda_index:
| Column | Purpose |
|---|---|
| facet_id | Foreign key to wp_gridpanda_facets.id |
| post_id | The WordPress post that has this value |
| facet_value | The raw filterable value (term slug, meta value, etc.) |
| facet_display | Human-readable label shown in the facet widget |
| facet_order | Sort order (term_order for taxonomies, 0 for meta) |
| depth | Tree depth for hierarchy facets (0 = root) |
| parent_id | WP term_id of parent for hierarchy facets |
| language | Language code for WPML/Polylang multilingual indexing |
At query time, Grid Panda uses the index to calculate facet counts: for each candidate value, it counts how many posts in the current result set have that value. For active filters, it runs a complementary query that excludes the facet's own filter so sibling values remain visible with accurate counts.
REST API
/wp-json/gridpanda/v1/facetsList all facets (admin sees all, public sees active only)
/wp-json/gridpanda/v1/facets/{id}Get a single facet by ID
/wp-json/gridpanda/v1/facets/{id}/choicesGet facet choices with counts for a post ID set
/wp-json/gridpanda/v1/facetsCreate a new facet (admin)
/wp-json/gridpanda/v1/facets/{id}Update a facet (admin)
/wp-json/gridpanda/v1/facets/{id}Delete a facet (admin)
/wp-json/gridpanda/v1/facets/{id}/reindexTrigger reindex for a single facet (admin)
/wp-json/gridpanda/v1/facets/{id}/autocompleteFetch autocomplete suggestions (public)
