JetSmartFilters: Filter Users by a Related CCT Field and Fixed Status

To filter users by a related CCT field with JetSmartFilters, build an SQL/AI Query in the JetEngine Query Builder that returns only the IDs of matching users. Pass those IDs into a regular Users query with the Query Results macro. The fixed condition (for example status = approved) is a permanent Where clause in the SQL query. The visitor's choice reaches that query through one extra Where clause whose value is the JetSmartFilters Query macro, and the filter itself targets the Users query.
This follows the pattern Crocoblock documents for filters that combine data from different sources. A second option, filtering the SQL/AI query directly through Columns for filters, is covered near the end. Treat that one as something to test in your version.
- Why the filter ignores your second field
- Check your table and column names
- Step 1: Build the ID-source SQL query
- Step 2: Feed the IDs into a Users query
- Step 3: Connect the Listing Grid and the filter
- Test every filter state
- Alternative: filter the SQL query directly
- Troubleshooting
Why the filter ignores your second field
A radio or select filter sends one value. If the listing only knows about users, it has no way to check two fields on the same related CCT item. So put the fixed conditions (status = approved, the relation ID) inside an SQL query that joins the relation and CCT tables, where they apply on every request. Let the filter supply only the visitor's choice, as one more clause in that same query. Because every clause is checked against the same joined CCT row, a user matches only when one of their items is approved and belongs to the selected organization.
Check your table and column names
Each CCT has its own table, named wp_jet_cct_ plus the CCT slug (for example wp_jet_cct_memberships). Its ID column is _ID, and it usually includes a system column cct_status that marks items as published or draft. Relations without their own table share one default table, usually jet_rel_default, which stores ID pairs in columns such as rel_id, parent_object_id and child_object_id. If Register separate DB table is enabled for the relation, its rows live in the relation's own table instead. Confirm all of this in phpMyAdmin or in the From Table dropdown of an SQL/AI Query:
- The table that holds your relation's rows, and the exact CCT table name.
- The column names of your organization and status fields, and the exact value
cct_statusstores for published items. - Which side of the relation the user is on. Here the user is the parent and the CCT item is the child. If yours is reversed, swap the parent and child columns everywhere.
- The relation ID. You may see it in the relation's edit URL; confirm it against the
rel_idvalues in the relations table. - The values your select fields store (for example
org_acme), not the labels visitors see (Acme Ltd).
Step 1: Build the ID-source SQL query
- Go to WordPress Dashboard > JetEngine > JetEngine > Query Builder, add a query, set the type to SQL/AI Query and leave Advanced/AI mode off.
- In From Table, choose the relations table you confirmed.
- Turn on Use Join and add a row: Join Type Inner Join, Join Table = your CCT table, When current table column =
child_object_id, Is equal to other table column =_ID. - Add a second row: Inner Join, Join Table = the users table,
parent_object_idequal toID. - Under Where (query clauses), add the fixed conditions:
- Your status column, Compare Equal, Value
approved, Type Char. - If the relation uses the shared table:
rel_id, Compare Equal, Value = your relation ID, Type Numeric. cct_status, Compare Equal, Value = the published value you found, Type Char. This stops draft items marked approved from qualifying a user.
- Your status column, Compare Equal, Value
- Add one more Where clause on the organization column, Compare Equal, Type Char. Click the database icon next to Value and choose JetSmartFilters Query. Set Type to Plain Query Var, Variable Name to
org_field, and Result Format to Plain (the two choices are Plain and Prepare for SQL). Leave Fallback empty, then turn on Exclude this clause from the query if dynamic value is empty, a toggle that appears only for dynamic values. The JetSmartFilters Macros Guide doesn't describe how either format escapes values, so keep the macro in this builder-generated clause, never type it into hand-written SQL, and test the result. - In Include columns, pick only the user ID column. Left empty, the query returns all possible columns, which may include columns from every joined table, possibly sensitive users-table columns such as
user_pass. Check the preview to see what you get, and write down the exact name the preview shows for the user ID column. With joined tables it may carry a table prefix or alias, and you need that exact name in Step 2. - Turn on Group Results and choose the user ID column in Group By, so each user appears once. If Include columns also contained a CCT or relation column, servers with ONLY_FULL_GROUP_BY in
sql_modemay throw an error. - Leave Show/Per Page Limit, Total Query Limit and Offset empty on this query. Show/Per Page Limit or Total Query Limit (or Offset together with Total Query Limit) can cut the ID list; Offset on its own does nothing. A limit here may mean Query Results passes only part of your user IDs, so leaving all three empty avoids the risk. Pagination belongs on the Users query.
- Keep Register Rest API Endpoint off, because it makes the query results publicly accessible.
- Turn off Cache Query while testing, save, and note the query's ID from its edit URL.
The preview should show one user ID per approved user. With no request variable present, the organization clause is excluded, so the fixed conditions alone decide the result.
Step 2: Feed the IDs into a Users query
- In the Query Builder, add a query with the type Users Query.
- Find the Include field (it sits on the Include/Exclude tab; check the tab label in your version). Open Dynamic Tags and choose Query Results. Pick it from the list rather than typing it, because typed macros aren't supported in the Query Builder. Select your SQL query, use the selected format, and as the property pick the user ID column name exactly as the SQL query preview showed it. With a column named
IDand an SQL query with the ID 12, the result looks like%query_results|12|selected|ID%. - Set the macro's Fallback to
2147483647, a user ID that cannot exist on your site. Don't use0: WordPress skips the Include setting when its value is empty, and in PHP the string "0" counts as empty, so a no-match state could list every user, admins included. Never use a negative number either, because WordPress converts it to a positive ID (-1becomes user 1). You will confirm the fallback in the no-match test below. - Set how many users show per page on this query, give it a Custom Query ID such as
approved_members, and save.
Step 3: Connect the Listing Grid and the filter
Run JetSmartFilters 3.8.3.1 or newer. Versions up to 3.8.3 have an unauthenticated SQL injection (CVE-2026-56067), and this setup sends visitor values into a query.
- Create a listing item for users and place it in a Listing Grid that uses the Users query.
- In Elementor, enter the query ID
approved_membersin the Listing Grid's CSS ID field so the filter can find the listing. This tip covers Elementor; in another builder, set the listing's query ID in that builder's own listing settings. - Add a Radio or Select filter. Set its This filter for option to JetEngine, and set its Query ID to exactly the Users query's Custom Query ID.
- In the filter's Query Variable, click the dynamic tag icon and look for the plain query variable option, spelled Plane Query Variable in the UI, then enter
org_field. The field should then store_plain_query::org_field. Crocoblock documents this option for the Search filter, so confirm that it shows up in the Query Variable of a Radio or Select filter in your version. If it doesn't, the direct route below is the other option. - Make every option value match a stored select value. Our tip on filtering a text field with a select filter covers the same values-versus-labels issue.
Test every filter state
- No option selected: every approved user, each listed once.
- An option selected: only users with an approved item from that organization.
- An option with no matching approved items: an empty listing, because the Include field receives the fallback ID
2147483647, which matches no user. If every user appears instead, the fallback isn't reaching the Users query. Re-check the Fallback value and the selected property name, and until it works, limit the filter's options to organizations that have approved items.
If visitors really need to choose the status, remove its fixed Where clause, add a second JetSmartFilters Query clause for the status column with its own variable name, and add a second filter with the same Query ID.
Alternative: filter the SQL query directly
You can also point the listing and the filter at the SQL/AI query itself. The closest official example filters a joined relation-meta column, not a joined CCT field, and the Macros Guide notes that JetSmartFilters sometimes struggles to filter advanced SQL queries directly. Test every state above before you rely on it.
- Build the query as in Step 1, but skip the JetSmartFilters Query clause.
- In Include columns, pick only the users-table columns your listing shows.
- In Columns for filters, select the organization column and copy its name exactly as the list shows it. Check that joined CCT columns appear there in your version.
- Set Cast result to instance of object to User, set Show/Per Page Limit for the listing, and add a Custom Query ID.
- Point the Listing Grid (including its CSS ID in Elementor) and the filter's Query ID at this query, set This filter for to JetEngine, and paste the column name into the filter's Query Variable.
Our tip on a custom visual filter built on an SQL query covers related ground, and the SQL/AI Query documentation lists every setting used here.
Troubleshooting
- The filter doesn't change the listing. The filter's This filter for option isn't set to JetEngine, the Listing Grid's CSS ID doesn't hold the query ID, the filter's Query ID doesn't match the Custom Query ID exactly, or the Query Variable doesn't store
_plain_query::org_fieldwith the same name as the macro's Variable Name. - All approved users show whatever you pick. The JetSmartFilters Query macro is empty, so its clause is excluded. Re-check the plain query variable tag and the option values.
- Every user on the site appears, admins included. The Include field received an empty value. Check that the Query Results fallback is
2147483647, not0, and that the selected property matches the column name in the SQL preview. Official docs don't say whether an empty result triggers the macro's Fallback, so if it never applies on your site, limit the filter's options to organizations that have approved items. - Empty listing, no error. You're joining the shared relations table when the relation has its own table, the relation ID is wrong, the user sits on the other side of the relation, the selected property name is wrong, or the status or
cct_statusvalue doesn't match what's stored. - Some approved users never appear. Show/Per Page Limit or Total Query Limit is set on the ID-source SQL query (Offset only counts together with Total Query Limit), so it may pass only part of the IDs. Clear all three and paginate the Users query.
- The same user appears several times. Group Results is off, or Group By isn't set to the user ID column.
- SQL error mentioning GROUP BY. Include columns contains a CCT or relation column. Keep only the user ID (or users-table columns). Our list of common JetEngine Query Builder mistakes covers more checks.
- Results look stale. Turn off Cache Query while editing. This matters most with an object cache such as Redis or Memcached.
- A range filter does nothing. The
%jsf_filter_query%macro has been reported not to work with the range filter. The reply in that issue links a Crocoblock gist that uses--range,--range1and--range2suffixes. Check the gist for how to install it and test it against your version.
Once every filter state returns the right users, turn Cache Query back on for both queries. If you'd rather have this built and tested on your own site, post a request on BoostMyCroco.
Frequently Asked Questions
Can JetSmartFilters filter a JetEngine users listing by fields stored in a CCT?
Do I need to know SQL to set this up?
Why does my filter show all approved users no matter which option I pick?
Why are some matching users missing from the listing?
Will this work with a checkbox or range filter instead of a radio?
This guide answers a question from our community: Help needed with SQL query for JetSmartFilters
Sources
- Macros Guide — JetSmartFilters (crocoblock.com)
- SQL/AI Query Type of Query Builder — JetEngine (crocoblock.com)
- Jetengine Jetsmartfilter macro %jsf_filter_query% · Issue #7379 · Crocoblock/suggestions · GitHub (github.com)


