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:

ColumnTypeDescription
idbigint unsignedAuto-increment primary key
namevarchar(255)Human-readable label shown above the filter widget
slugvarchar(200) UNIQUEURL-safe identifier used in fx_ query params and clean URLs
typevarchar(50)Filter widget type: checkbox, radio, range, color, etc.
sourcevarchar(255)Data source: taxonomy, post_meta, post_field, or wc
configlongtext (JSON)Type-specific configuration as a JSON object
post_typevarchar(100)WordPress post type this facet indexes against
display_orderintSort order in the facets admin list
created_at / updated_atdatetimeRecord 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:

Taxonomytaxonomy:{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_color

Query output: tax_query: [{ taxonomy, field: 'slug', terms, operator: 'IN' }]

Post Metapost_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_color

Query output: meta_query: [{ key, value, compare: 'IN' or 'BETWEEN' }]

Post Fieldpost_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_status

Query output: author__in, post_status, date_query, etc.

WooCommercewc:{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_size

Query 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:

checkbox

Multi-select checkboxes with optional hierarchy, count display, and show-more truncation. Supports OR/AND/NOT operators.

radio

Single-select radio buttons with an optional 'All' option that clears the filter. Single value only.

dropdown

Native <select> element with single or multi-select mode, optional placeholder, and count display.

button_group

Inline pill buttons with configurable size (sm/md/lg), wrap or horizontal scroll layout, single or multi-select.

range

Dual-handle slider for numeric ranges with configurable prefix/suffix, decimal precision, and step size.

date_range

Date picker inputs or preset buttons (last 7 days, this month, etc.) for filtering by post date or date meta fields.

search

Text input for full-text search or field-scoped search (title only, meta LIKE, taxonomy term search).

autocomplete

AJAX typeahead that fetches matching values from the index as the user types. Supports single and multi-select.

rating

Star rating filter (1–5 or custom max). Supports 'and up' (gte) or exact match mode. Aggregates tier counts from index.

color

Color swatch grid/list with configurable size, shape (circle/square/rounded), and hex color or gradient mapping.

toggle

Binary on/off switch that filters by a specific field value (e.g. is_featured=1, stock_status=instock).

hierarchy

Collapsible tree for hierarchical taxonomies. Shows parent → child relationships from the index depth/parent_id columns.

sort

Sort order control rendered as dropdown, radio, or button group. Modifies WP_Query orderby/order rather than filtering.

active_filters

Displays 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:

ColumnPurpose
facet_idForeign key to wp_gridpanda_facets.id
post_idThe WordPress post that has this value
facet_valueThe raw filterable value (term slug, meta value, etc.)
facet_displayHuman-readable label shown in the facet widget
facet_orderSort order (term_order for taxonomies, 0 for meta)
depthTree depth for hierarchy facets (0 = root)
parent_idWP term_id of parent for hierarchy facets
languageLanguage 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

GET
/wp-json/gridpanda/v1/facets

List all facets (admin sees all, public sees active only)

GET
/wp-json/gridpanda/v1/facets/{id}

Get a single facet by ID

GET
/wp-json/gridpanda/v1/facets/{id}/choices

Get facet choices with counts for a post ID set

POST
/wp-json/gridpanda/v1/facets

Create a new facet (admin)

PUT
/wp-json/gridpanda/v1/facets/{id}

Update a facet (admin)

DELETE
/wp-json/gridpanda/v1/facets/{id}

Delete a facet (admin)

POST
/wp-json/gridpanda/v1/facets/{id}/reindex

Trigger reindex for a single facet (admin)

GET
/wp-json/gridpanda/v1/facets/{id}/autocomplete

Fetch autocomplete suggestions (public)