Merge multiple WP_Query results into an array with no duplicates - wordpress

I have a custom post type with its own custom template, and I want to display blog posts that are related to the custom post type title. However, I want to display a referenced post first, then search for any posts with tags, and then find relevant posts after that. Any one of these could result in no results or many. The total number of posts I want to display is 6.
`
$searchtag = strtolower(get_the_title());
$arg1 = array(
'post_type' => 'post',
'p' => $post_id,
);
$arg2 = array(
'post_type' => 'post',
'tag' => $searchtag,
'post_status' => 'publish',
'orderby' => 'post_date',
'order' => 'desc',
'posts_per_page' => 6,
);
$arg3 = array(
'post_type' => 'post',
's' => $searchtag,
'post_status' => 'publish',
'orderby' => 'relevance',
'posts_per_page' => 6,
);
// Get the posts
$query1 = new wp_query( $arg1 );
$query2 = new wp_query( $arg2 );
$query3 = new wp_query( $arg3 );
$related_query = new wp_query();
// Merge the unique posts
$related_query->posts = array_merge($query1->posts, $query2->posts, $query3->posts);
$related_query->post_count = $query1->post_count + $query2->post_count + $query3->post_count;
if($related_query->have_posts):
`
I read an article that stated that using post__not_in was taxing on the database and I should not incorporate it.
Do I need to add logic if the query->post_count = 0?
How can I maintain the order but ensure that there are no duplicates displayed?
I get duplicates in these results currently. I'd like to eliminate them while keeping the order of the posts by query1, then query2 then query3... displaying the first 6 posts.

The better way is make 3 queries with parameter:
'fields' => 'ids'
and there:
$postIds1 = get_posts($args1);
when merge got data:
$postIds = array_merge(array(0), $postIds1, $postIds2, $postIds3);
and when make forth request with
'post__in' => $postIds

Related

WordPress WP QUERY with OR condition

On my website, there are a couple of custom post types with default blog posts. On the blog posts, there are many categories.
One of the post types is webinar. I like to query all posts from the post type webinar and those blog posts that have category id 43
I tried following way but does not work.
1:
$query = new WP_Query(array(
'post_type' => array('webinar'), // I need all post from webinar
'cat' => '43', // I need all blog post under this category (Post type post category id 43)
);
Result: No post found
2:
$query = new WP_Query(array(
'post_type' => array('webinar','post'), // I need all post from webinar
'cat' => '43', // I need all blog post under this category (Post type post category id 43)
);
Result: Only posts of category id 43, no post from post types webinar
I need something like the following:
$query = new WP_Query(array(
array(
'relation' => 'OR',
array(
'post_type' => array('webinar')
),
array(
'post_type' => 'post',
'cat' => '43'
)
)
));
How can I do this?
Solution 1
// This could be cached in the site option
// in case this code will take a long time.
$webinar_terms = get_terms([
'taxonomy' => '{webinar_tax_name}',
'hide_empty' => true,
]);
$allowed_terms_ids = [];
foreach ( $webinar_terms as $term )
{
$allowed_terms_ids[] = $term->term_id;
}
// Add post cat to allowed terms.
$allowed_terms_ids[] = 43;
$query = new WP_Query([
'post_type' => ['webinar','post'],
'cat' => $allowed_terms_ids,
]);
NOTE
This will work only in case all webinar posts will be assigned to at least 1 {webinar_tax_name} term.
Solution 2
You may use posts_request hook and update SQL for 1 of your queries in your example. But I would prefer 1st solution as it will be more clear for other devs.
$posts = get_posts([
'post_type' => 'webinar',
'post_status' => 'publish',
'numberposts' => -1
]);
$post_data = get_posts( [
'post_type' => ['webinar', 'post'],
'posts_per_page' => - 1,
'tax_query' => array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => '43'
),
] );
Would something like this work? I have not tested this so may need playing with a bit.
If you dont want to use get_posts then can always use new WP_Query but will need to ammend

The most read book custom post (find the top post with the same title)

As the title says, i have a custom post type named book.
Each user can create another custom post type named read_book only if the "book" post type with that name exist.
So now i'm trying to display the most read book by getting that post name which has the most occurrences with the same title.
Is that possible ?
Thank you!
PS: If i would have 3 read_books with the name A, 2 with the name B and one with the name C, i would like to display the book post with the name A.
Edit: This is how it should look
// initial query to get the title
$read_books = new WP_Query(array(
'post_type' => 'read_book',
'posts_per_page' => 1, // only the title is needed, so one post is enough
'meta_key' => 'title', // here should happen the magic
'orderby' => 'meta_value_num',
'order' => 'DESC',
'date_query' => array( // optional ( most read book from past 30 days )
'after' => date('Y-m-d', strtotime('-30 days')), // don't query the whole database
)
));
if ($read_books->have_posts()):
while ($read_books->have_posts()): $read_books->the_post();
$book_title = get_the_title;
endwhile;
endif;
// final query
$most_read_book = new WP_Query(array(
'post_type' => 'book',
's' => $book_title; // it should be only one book with that title because users can't add custom titles
'posts_per_page' => 1,
));
if ($most_read_book->have_posts()):
while ($most_read_book->have_posts()): $most_read_book->the_post();
// book info displayed on frontend
endwhile;
endif;
I found a solution, but i'm not sure if is the best one. Yet, it works just fine.
Maybe there's a faster one.
//get the title string
$booksQuery = new WP_Query(array(
'post_type' => 'read_book',
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => -1,
'post_status' => 'publish',
'no_found_rows' => true,
'date_query' => array(
'after' => date('Y-m-d', strtotime('-90 days')),
)
));
$allTitles = array();
if ($booksQuery->have_posts()):
while ($booksQuery->have_posts()): $booksQuery->the_post();
$allTitles[] = get_the_title();
endwhile;
endif;
$allTitles = array_count_values($allTitles); // group titles
asort($allTitles); // sort titles by number of occurrences
$topTitle = key( array_slice( $allTitles, -1, 1, TRUE ) ); // get the needed title
$finalTitle = str_replace([' – ', ' — ', ' - '], ' ', $topTitle); // the title is stored with a different character in database, so just remove it from title
//final query
$mrQuery = new WP_Query(array(
's' => $finalTitle,
'post_type' => 'book',
'posts_per_page' => 1,
'post_status' => 'publish',
'no_found_rows' => true,
));
// the frontend code here..

WP_Query on different post_types with additional conditions per post_type

in short I have the following situation: products that are posted by the users which have a custom post type. Call them "ads". Ads have a 1-1 link to WooCommerce Products.
Next to that I have WooCommerce Products that are Vendor Item (they are added by a multiple vendor plugin) and distinguish from other post_type products with their meta_key "_vendor_product" set to 1.
So far I made 2 queries and added them:
$ads = new WP_Query(
array(
'post_type' => 'ads',
'paged' => $paged,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'product_id',
'value' => $product_ids,
'compare' => 'IN'
)
)
)
);
$shop_items = new WP_Query(
array(
'post_type' => 'product',
'paged' => $paged,
'post__in' => $product_ids,
'meta_query' => array(
array(
'key' => '_vendor_product',
'value' => 1,
'compare' => '='
)
)
)
);
}
// Include shop items in the stream of products
$wp_query = new WP_Query(array('paged' => $paged));
$wp_query->posts = array_merge( $ads->posts, $shop_items->posts );
$wp_query->post_count = $ads->post_count + $shop_items->post_count;
But I need them both in one WP_Query, as otherwise somehwere my actions/filters/pagination seem to break.
I know I can query different post_types by 'post_type' => array( 'ads', 'product' ) but the condition of _vendor_product = 1 only applies to the items of post_type product and not to ads.
What I need is basically
SELECT * FROM wp_posts, wp_postmeta
WHERE (post_type = 'ads')
OR
(post_type = 'product' AND meta_key = '_vendor_product' AND meta_value = 1)
AND
wp_posts.ID = wp_postmeta.post_id
Is it possible to achieve this within the boundaries of WP_Query?
I achieved my result basically the same way Blackbam suggested, although without directly involving SQL.
If anyone ever finds this I will post my solution as it gave me a bit of a headache to figure out why my results were limited to 10 when using get_posts on WP_Query (the solution was 'nopaging' in WP_Query.
$ps_ads = new WP_Query(
array(
'post_type' => 'ads',
'fields' => 'ids',
'nopaging' => true,
'post_status' => 'publish'
)
);
$shop_items = new WP_Query(
array(
'post_type' => 'product',
'fields' => 'ids',
'nopaging' => true,
'post__in' => $product_ids,
'meta_query' => array(
array(
'key' => '_vendor_product',
'value' => 1,
'compare' => '='
)
)
)
);
}
// Sum up all IDs
$ad_ids = $ps_ads->get_posts();
$shop_items_ids = $shop_items->get_posts();
$ids = array_merge($ad_ids, $shop_items_ids);
$wp_query = new WP_Query(
array(
'post_type' => array( 'ads' , 'product' ),
'paged' => $paged,
'post__in' => $ids
)
);
Hope it helps.
I doubt that you can achieve it within one query directly (however somebody may bring up a solution). However I suggest an easy workaround to this problem - find out the post IDs and make a single query from that:
global $wpdb;
$all_needed = $wpdb->get_results('SELECT wp_posts.id FROM wp_posts, wp_postmeta
WHERE (post_type = 'ads')
OR
(post_type = 'product' AND meta_key = '_vendor_product' AND meta_value = 1)
AND
wp_posts.ID = wp_postmeta.post_id');
// extract_ids is a pseudo-code function - check what the result actually is and make an array of post_ids
$post_ids = extract_ids($all_needed)
// now you have your single query
$query = new WP_Query(array('post__in'=>$post_ids));
// proceed ....

wordpress get post where category is not equal to blog

i am trying to get the latest post which is not a blog . I know how to get a post by category name but how to get the post which is not equal to a specific category name . Here is the code i use to get for a category name
$query = array (
'category_name' => 'Blog',
'posts_per_page' => 1,
'post_status' => array('publish')
);
As stated in the Codex (http://codex.wordpress.org/Class_Reference/WP_Query#Category_Parameters) you can use 'category__not_in' and feed it the id of the category you want to exclude.
$args = array(
'category__not_in' => array($id), // Pass in one or more IDs here.
'posts_per_page' => 1,
'post_status' => 'publish', // no need to pass it in a array
'orderby' => 'date' // Since you wanted the latest post, this is default value so not really needed
);
You can find out the ID of the category by hovering the category-link in the WP-backend and go to the category and then check the adress in your adress-bar, it should look something like this:
/wp-admin/edit-tags.php?action=edit&taxonomy=category&tag_ID=2
There's a category_not_in query parameter. To use it you'll need the ID, not the slug, of the category you want to exclude from your result.
http://codex.wordpress.org/Class_Reference/WP_Query#Category_Parameters
$BlogObj = get_category_by_slug('Blog');
$query = array (
'category_not_in' => array($BlogObj->term_id),
'posts_per_page' => 1,
'post_status' => array('publish')
);
You may try this:
$query = array (
'category_not_in' => array(get_cat_ID('Blog'),
'posts_per_page' => 1,
'post_status' => 'publish'
);
Think we need to combine answers of #the-alpha and #o-jones here.
$BlogObj = get_category_by_slug('Blog');
$query = array (
'category__not_in' => array($BlogObj->term_id),
'posts_per_page' => 1,
'post_status' => array('publish')
);
Simply you have to change 'category__not_in' instead of 'category_not_in'. Adding this for future users may come around.

Custom Wordpress query using WP_Query()

I'm trying to get a custom query going in wordpress.
Basically, I want to select all custom post types where the variable "name" has been set to "sean".
I have tried the following :
$my_loop = new WP_Query( array( 'post_type' => 'my_post', 'meta_value=sean',
'posts_per_page' => 15, 'orderby' => 'id', 'order' => 'DESC' ) );
I got this from the wordpress codex :
Display posts where the custom field value is 'blue', regardless of the custom field key:
$query = new WP_Query( 'meta_value=blue' );
Any Help would be appreciated
EDIT: I should add that I do indeed have a wordpress loop using :
while ( $my_loop->have_posts() ) {
$pdf_loop->the_post();.... etc
Thanks again,
Dave
You are mixing query string and array style arguments. Try either
new WP_Query(array(
'post_type' => 'my_post',
'meta_value' => 'sean',
'posts_per_page' => 15,
'orderby' => 'id',
'order' => 'DESC'
));
Or
new WP_Query('post_type=my_post&meta_value=sean&posts_per_page=15&orderby=id&order=DESC');

Resources