Skip to content

🐞 Bug: Published listings without _featured meta are excluded from archive queries #2965

Description

@RabbiIslamRony

Directorist version

8.9.3

Environment

  • WordPress 7.0.4
  • PHP 8.2.29
  • LocalWP
  • Directorist Core with Featured Listings enabled
  • Reproduced without a pricing-plan query provider overriding Core featured ordering

Summary

A published listing without an _featured post-meta record can be opened through its direct URL but is excluded from normal All Listings/archive queries.

A missing _featured value should mean “not featured.” Instead, the current Core query requires the meta key to exist when featured ordering is enabled.

Adding _featured = 0 to the same listing makes it appear immediately.

User impact

  • Published listing counts can disagree with the listings visible in the frontend grid.
  • A newly created, imported, migrated, or programmatically inserted listing may have a working direct URL but remain undiscoverable in archives and search.
  • Sorting differently does not reliably restore the listing because featured ordering is applied across multiple order modes.
  • The failure is silent; there is no validation or admin warning about the missing meta row.

Reproduction setup

Enable Featured Listings and create two otherwise identical published listings:

Listing _featured state
A 0
B Meta row does not exist

Both listings must have a valid directory type and any required non-expired state.

Steps to reproduce

  1. Open both listings through their direct URLs.
  2. Confirm both are publicly accessible.
  3. Load the All Listings page.
  4. Search for Listing B by its exact title.
  5. Add _featured = 0 to Listing B.
  6. Reload the same archive/search request.

Actual result

State Direct URL All Listings/archive
_featured = 0 Visible Visible
Missing _featured Visible Missing
Missing meta changed to 0 Visible Visible

Expected result

Listings with missing _featured metadata must be treated as non-featured and remain in all normal archive/search result sets.

Only an explicit featured-only request should exclude them.

Root cause

There are two Core query mechanisms that can independently require the meta row.

1. Normal archive meta query

Directorist\Directorist_Listings::execute_meta_query_args() adds this clause whenever Featured Listings are enabled and the request is not explicitly filtering to featured listings:

$meta_queries['_featured'] = [
    'key'     => '_featured',
    'type'    => 'NUMERIC',
    'compare' => 'EXISTS',
];

This turns featured ordering into a filtering requirement.

2. Featured sorting through meta_key

Several order modes assign:

$args['meta_key'] = '_featured';

and then order by meta_value_num.

This appears in title, date, random, and search ordering paths. WordPress joins the postmeta table for that key, so posts with no matching row can be excluded even if the explicit EXISTS meta query is removed.

Therefore, removing only the EXISTS clause may not fully fix the bug.

Known affected query paths

Review at minimum:

  • Directorist_Listings::parse_query_args()
  • Directorist_Listings::parse_search_query_args()
  • Directorist_Listings::execute_meta_query_args()
  • Title ascending/descending
  • Date ascending/descending
  • Random
  • Price
  • Popular/views/rating ordering
  • Exact-title search
  • Pagination/count queries

Why the metadata can be missing

The current admin metabox normally writes _featured = false, but missing rows can still exist because of:

  • Legacy listings created before all save paths initialized the meta.
  • CSV/import or migration paths.
  • REST/API or custom integrations.
  • Direct wp_insert_post() usage.
  • Interrupted/incomplete listing creation.
  • Third-party extensions that create listings without the standard Core form handler.

The read query should tolerate these records even if all current write paths are hardened.

Extension interaction

Directorist Pricing Plans 4.0.1 currently filters directorist_query_arg_has_featured to false on frontend requests and provides its own featured/priority sorting through a LEFT JOIN and COALESCE(..., 0).

That integration can mask this Core bug while it is active. The issue reproduces in the Core query path when that provider is absent, inactive, older, or does not intercept the request.

A Core fix must avoid double joins or ordering conflicts when Pricing Plans supplies its own query strategy.

Suggested implementation direction

A complete fix should include both query hardening and data hardening.

A. Query hardening

For normal archives, do not require _featured to exist.

Treat missing values as zero during ordering. A robust strategy is:

  • Use a scoped LEFT JOIN for _featured.
  • Order by a numeric expression equivalent to COALESCE( featured_value, 0 ) DESC.
  • Preserve the selected secondary order, such as title/date/price.
  • Prevent duplicates if historical data contains more than one _featured row.
  • Ensure found rows and pagination totals remain correct.

The Pricing Plans provider already demonstrates a compatible left-join/COALESCE approach, but Core should implement its own narrowly scoped strategy or reusable query helper without coupling to the extension.

A meta-query EXISTS OR NOT EXISTS solution may keep both record types in the result, but it must be validated carefully across named-clause ordering and all supported sort modes.

B. Featured-only behavior

Explicit featured-only requests should remain strict:

_featured = 1

Missing or zero values must not be returned by featured-only queries.

C. Write-path hardening

Audit all Core listing creation/update paths and initialize:

_featured = 0

when the listing is not featured.

Include admin, frontend submission, REST, import, migration, renewal, duplicate/clone, and any background creation paths.

D. Optional migration

Consider a versioned, batch-safe migration that backfills _featured = 0 for existing listings missing the key.

The read-query fix is still required because custom integrations can create incomplete metadata after a migration. Any migration must avoid one unbounded update on large directories.

Compatibility and regression risks

  • Do not break Pricing Plans display-priority ordering.
  • Preserve featured-first ordering for _featured = 1.
  • Preserve the user-selected secondary sort.
  • Avoid duplicate posts caused by postmeta joins.
  • Keep pagination totals and found_posts accurate.
  • Keep multi-directory filtering intact.
  • Do not make featured-only queries include missing values.
  • Verify search and normal archive queries separately.
  • Ensure admin/non-frontend queries using directorist_query_arg_has_featured remain correct.

Test matrix

Use at least three listings in the same directory:

Listing Featured meta
Featured 1
Normal 0
Legacy/missing No row

For every normal archive/search query:

  • All three listings are returned.
  • Featured listing is first when featured ordering applies.
  • Normal and missing-meta listings follow the selected secondary order.

Test the following sorts:

  • Title ascending
  • Title descending
  • Date ascending
  • Date descending
  • Random
  • Price ascending/descending
  • Views/popular
  • Rating
  • Exact-title search

Also verify:

  • Featured-only returns only the 1 listing.
  • Pagination totals count all normal eligible listings.
  • No duplicates are returned.
  • Results are correct with and without Pricing Plans active.
  • Different directory types do not leak into each other.
  • A newly imported/API-created listing without the meta remains visible.

Acceptance criteria

  • A published listing with no _featured row appears in normal archives and search.
  • Missing featured metadata behaves as numeric zero for ordering.
  • Featured listings retain featured-first ordering.
  • Featured-only queries remain strict.
  • All supported order modes and pagination remain correct.
  • Current extensions can override/enhance ordering without conflict.
  • Core write paths initialize the default meta.
  • Regression coverage is added for both archive and search queries.

Isolating the problem

  • I have reproduced this bug in localhost.
  • This bug happens with a default WordPress theme active.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions