0

The following tax_query returns only matched posts (with 'matchedstring' IN taxonomy array):

function only_returns_matched_posts( $query ) {

    if( !$query->is_main_query() || is_admin() )
        return;
    
    $taxquery = array(
        array(
            'taxonomy' => 'mygroup',
            'field' => 'slug',
            'terms' => 'matchedstring',
            'compare'=> 'IN'
        )
    );
    $query->set( 'tax_query', $taxquery );
    
}
add_action( 'pre_get_posts', 'only_returns_matched_posts' );

I want the matched posts grouped at the top of the query with the other posts following. Is it possible to either:

  • use this format with a orderby
  • do 2 separate queries and merge them
  • use a custom Group By SQL query

EDIT

I managed to merge 2 queries but I lose the menu_order when I apply post__in to keep the $queryA + $queryB order. Should I get ids differently than with $query->posts to keep original menu_order of the queries?

function group_matched_posts_at_top( $query ) {

  // Check if this is the main query and not in the admin area
  if( !$query->is_main_query() || is_admin() )
    return;

  // Get posts with matched taxonomy + posts without taxonomy
  $queryAparams = array(
    'posts_per_page' => -1,
    'post_type' => 'product',
    'order_by' => 'menu_order',
    'order' => 'ASC',
    'fields' => 'ids',
    'tax_query'=> array(
      'relation' => 'OR',
      array(
        'taxonomy' => 'pa_group',
        'field' => 'slug',
        'terms' => 'matchedstring',
        'operator' => 'IN'
      ),
      array(
        'taxonomy' => 'pa_group',
        'operator' => 'NOT EXISTS'
      )
    )
  );

  // Get posts with other taxonomies
  $queryBparams = array(
    'posts_per_page' => -1,
    'post_type' => 'product',
    'order_by' => 'menu_order',
    'order' => 'ASC',
    'fields' => 'ids',
    'tax_query'=> array(
      'relation' => 'AND',
      array(
        'taxonomy' => 'pa_group',
        'field' => 'slug',
        'terms' => 'matchedstring',
        'operator' => 'NOT IN'
      ),
      array(
        'taxonomy' => 'pa_group',
        'operator' => 'EXISTS'
      )
    )
  );

  $queryA = new WP_Query($queryAparams);
  $queryB = new WP_Query($queryBparams);
  
  // Merging ids
  $postIDs = array_merge($queryA->posts,$queryB->posts);

  if(!empty($postIDs)){
      $query->set('post__in', $postIDs);
      $query->set('orderby', 'post__in'); 
  }

}
add_action( 'woocommerce_product_query', 'group_matched_posts_at_top' );

EDIT2

I'll post my own answer. I had to actually remove the 'fields' => 'ids' parameters to keep the queries menu_order and pluck the ids after resorting them.

6
  • I tried editing your question to avoid offtopic things but it might not be fully possible depending on the context, WooCommerce/3rd party plugin dev support questions are offtopic here and not in this stacks scope. I also don't see any tax_query in your question
    – Tom J Nowell
    Commented Feb 16, 2023 at 12:54
  • It is an illustration of what I need, my code uses how a tax_query is formated, but it is an invalid code because I think groupby is not a valid property in this context. It would be orderby, which doesn't accept the same array as a tax_query. Do you get the idea?
    – mikmikmik
    Commented Feb 16, 2023 at 13:47
  • 1
    yes and no, I'd strongly recommend fixing the question as the current code snippet is highly misleading, avoid hiding things and over-abstracting
    – Tom J Nowell
    Commented Feb 16, 2023 at 13:56
  • I simplified it
    – mikmikmik
    Commented Feb 16, 2023 at 14:53
  • Edit with merging 2 queries
    – mikmikmik
    Commented Feb 19, 2023 at 18:28

2 Answers 2

1

You could try modifying the tax query to use the relation parameter and add a second clause that matches any post that does not have the matched string value in the meta array.

See Taxonomy Parameters.

EDIT: Thank you for pointing that out, Tom. You're correct, I've updated to reflect.

function group_matched_posts_at_top( $query ) {

// Check if this is the main query and not in the admin area
if( !$query->is_main_query() || is_admin() )
    return;

// Define the tax query with two clauses (matched and not matched)
$taxquery = array(
    'relation' => 'OR', // Set the relation to OR to include posts that match either clause
    array(
        'taxonomy' => 'mymeta',
        'field' => 'slug',
        'terms' => 'matchedstring',
        'operator' => 'IN' // use the operator parameter to specify the comparison operator
    ),
    array(
        'taxonomy' => 'mymeta',
        'field' => 'slug',
        'terms' => 'matchedstring',
        'operator' => 'NOT IN' // use the operator parameter to specify the comparison operator
    )
);

// Set the tax query and meta key parameters for the query
$query->set( 'tax_query', $taxquery );
$query->set( 'meta_key', 'mymeta' );

// Set the orderby parameter to sort by the value of the "mymeta" field in descending order (so that matched posts appear first), and then by date in descending order (so that the most recent posts appear first within each group).
$query->set( 'orderby', array( 'meta_value' => 'DESC', 'date' => 'DESC' ) );


add_action( 'pre_get_posts', 'group_matched_posts_at_top' );

The main changes are as follows:

  • I've used the "taxonomy" parameter to specify the taxonomy to query.

  • I've used the "operator" parameter instead of "compare" to specify the comparison operator (IN or NOT IN).

  • I've added the "field" parameter with a value of "slug" to specify that we're comparing the term slug (i.e., the term's "mymeta" field).

These changes should make the query work with a term/taxonomy meta field, which was not supported by my earlier solution.

6
  • 2
    this won't work as it relies on mymeta being post meta, but the OP says it's a term /taxonomy
    – Tom J Nowell
    Commented Feb 16, 2023 at 19:14
  • I see that you're nuking my answers elsewhere, I want to add, I wasn't aware using GPT in an assistive manner here on S.O. was banned, my apologies. Now I'm aware, I'll refrain from using it.
    – Lewis
    Commented Feb 16, 2023 at 19:37
  • Thank you for your answer, it doesn't seem to be working, the tax_query is working fine but the ordering is still based on a meta, which doesn't exist.
    – mikmikmik
    Commented Feb 19, 2023 at 16:46
  • I changed mymeta to mygroup sorry for the confusion
    – mikmikmik
    Commented Feb 19, 2023 at 16:49
  • It seems the most efficient way to go is to merge 2 queries and it doesn't seem doable inside pre_get_posts so I'll do it the woocommerce_product_query as stated here: stackoverflow.com/questions/60839648/…
    – mikmikmik
    Commented Feb 19, 2023 at 17:10
0

I finally managed to do it by merging 2 queries. I'm using it with a woocommerce attribute but it can work with any taxonomy.

function group_matched_posts_at_top( $query ) {

    // Modify the query only if it's the main query and it's a product archive page
    if ( !$query->is_main_query() || !(is_product_category() || is_shop())  || is_admin() ) {
        return;
    }

  // String to match
  $string_to_match = 'Whatever taxonomy';

  // Query for products with matched taxonomy
  $query1 = new WP_Query( array(
    'post_type' => 'product',
    'posts_per_page' => -1,
    'post_status' => 'publish',
    'order_by' => 'menu_order',
    'order' => 'ASC',
      'tax_query'=> array(
      'relation' => 'OR',
      array(
        'taxonomy' => 'pa_group',
        'field' => 'name',
        'terms' => $string_to_match,
        'operator' => 'IN'
      ),
      array(
        'taxonomy' => 'pa_group',
        'operator' => 'NOT EXISTS'
      )
    )
  ) );
  
  // Sorting first query before merging
  usort( $query1->posts, function( $a, $b ) {
    return $a->menu_order - $b->menu_order;
  } );

  // Query for other products
  $query2 = new WP_Query( array(
    'post_type' => 'product',
    'posts_per_page' => -1,
    'post_status' => 'publish',
    'order_by' => 'menu_order',
    'order' => 'ASC',
      'tax_query'=> array(
      'relation' => 'AND',
      array(
        'taxonomy' => 'pa_group',
        'field' => 'name',
        'terms' => $string_to_match,
        'operator' => 'NOT IN'
      ),
      array(
        'taxonomy' => 'pa_group',
        'operator' => 'EXISTS'
      )
    )
  ) );

  // Sorting second query before merging
  usort( $query2->posts, function( $a, $b ) {
    return $a->menu_order - $b->menu_order;
  } );

  // Merge the results in the desired order
  $products = array_merge( $query1->posts, $query2->posts );

  // Set the modified query results
  $query->set( 'posts_per_page', -1 );
  $query->set( 'post__in', wp_list_pluck( $products, 'ID' ) );
  // keep the order
  $query->set( 'orderby', 'post__in' );
}

add_action( 'pre_get_posts', 'group_matched_posts_at_top' );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.